Scripted widgets

You can create scripted widgets and add them to the dashboard widget gallery.

Overview

You can create scripted widgets and add them to the dashboard widget gallery.

A scripted widget is a web page embedded in an iframe on the dashboard. It can visualize OpenText Core Software Delivery Platform data using custom logic and JavaScript, provide custom layouts not available in the built-in gallery, and enable drill-down into entity lists.

A scripted widget can include one or more configuration pages where users define what the widget should display.

The widget communicates with OpenText Core Software Delivery Platform through a postMessage event protocol and can read and update data using the REST API.

Top-level workflow

  1. Design the widget

    Define the widget's purpose, data sources, user interactions, and configuration options.

  2. Build configuration pages

    Implement the HTML pages where users configure the widget scope, filters, and display options. See Implement configuration pages.

  3. Implement the widget display

    Implement the main display page that reads saved configuration, fetches data via REST, and renders the visualization. See Implement the display page.

  4. Add advanced capabilities

    Optionally integrate advanced filtering, color consistency, and drill-down. See Advanced filtering.

  5. Describe the widget in JSON

    Define the widget's identity, gallery metadata, display URL, and configuration pages. See JSON object.

  6. Deploy the widget

    Register the widget as an external action, either by uploading a bundle or defining it in the external action editor. See External actions.

The external app

A scripted widget consists of two types of web pages, both rendered as iframes:

  • Configuration pages: HTML pages where users define what the widget should show (entity type, filters, time range, grouping). Each page is presented as a tab in the widget's configuration dialog.

  • Display page: The main widget page rendered on the dashboard. It reads the saved configuration and renders the visualization.

Architecture context

  • Both page types run independently from OpenText Core Software Delivery Platform within embedded iframes.

  • They communicate with OpenText Core Software Delivery Platform exclusively using window.parent.postMessage.

  • The host provides context — including the widget's saved configuration and panel_id — via URL parameters and postMessage responses.

  • All REST API calls are made from the widget's JavaScript code using the current user's session.

  • Hosting model:

    • Widget files can be uploaded to OpenText Core Software Delivery Platform as a bundle and served using {bundle_url}.

    • Alternatively, widget files can be hosted on an external server. If hosted externally, trust must be established between OpenText Core Software Delivery Platform and the external server. For details, see CORS for external actions.

    • If the widget's backend needs to verify the invoking user, see User verification for external actions.

Supported events

Scripted widgets use a postMessage event protocol to exchange data and instructions with OpenText Core Software Delivery Platform. Events flow in both directions.

Preconditions and transport

  • Widget pages always run inside iframes, so post messages to the host using window.parent.

  • All messages must include the panel_id of the widget instance. OpenText Core Software Delivery Platform passes panel_id to the widget as a URL parameter at load time.

  • Configuration is stored per user and per widget instance; it persists across sessions.

Widget event reference

Configuration events

Event Direction Purpose
octane_read_widget_configuration Widget → OpenText Core Software Delivery Platform Request the current saved configuration for this widget instance. Send this when a configuration page or the display page loads.
octane_widget_configuration OpenText Core Software Delivery Platform → Widget OpenText Core Software Delivery Platform responds with the saved configuration. On first use, the configuration object is empty.
octane_update_widget_configuration Widget → OpenText Core Software Delivery Platform Persist an updated configuration object. Send this whenever the user changes a setting.

Advanced filter events

Event Direction Purpose
octane_show_filter_dialog Widget → OpenText Core Software Delivery Platform Request that OpenText Core Software Delivery Platform open the Advanced filter dialog for the specified entity type and scope.
octane_filter_dialog_closed OpenText Core Software Delivery Platform → Widget OpenText Core Software Delivery Platform notifies the widget that the user closed the filter dialog and provides the resulting filter object.
octane_convert_filter_to_query Widget → OpenText Core Software Delivery Platform Request conversion of a filter object to a REST query string for use in API calls.
octane_filter_converted_to_query OpenText Core Software Delivery Platform → Widget OpenText Core Software Delivery Platform responds with the converted query string.

Color and navigation events

Event Direction Purpose
octane_get_entity_color Widget → OpenText Core Software Delivery Platform Request the OpenText Core Software Delivery Platform UI colors for a list of entity type/ID pairs (for example, phase or severity values).
octane_entity_color OpenText Core Software Delivery Platform → Widget OpenText Core Software Delivery Platform responds with color values for the requested entities.
octane_display_entity_list Widget → OpenText Core Software Delivery Platform Open a drill-down dialog showing a filtered entity list. Triggered, for example, when the user clicks a data point in a chart.

Standard shared events (also applicable to widgets)

Event Direction Purpose
octane_refresh_entity Widget → OpenText Core Software Delivery Platform Instruct OpenText Core Software Delivery Platform to refresh specified entities.
octane_display_entity Widget → OpenText Core Software Delivery Platform Instruct OpenText Core Software Delivery Platform to open the details of a specified entity.
octane_report_error Widget → OpenText Core Software Delivery Platform Add an entry to the OpenText Core Software Delivery Platform errors log.

Note: If an entity is currently being edited with unsaved changes, some refresh operations may be intentionally suppressed to prevent data loss.

JSON object

The JSON object is the action definition that registers the scripted widget in the dashboard gallery and specifies the URLs of its display page and configuration pages.

The JSON object can either be part of an uploaded bundle or added directly in the external action editor. For details, see External actions.

Scripted widget JSON example

Copy code
ERROR: Invalid Code Highlighting Language

When this JSON is uploaded, the widget appears in the dashboard widget gallery (under My Organization) with the specified title and mode.description. When users click Configure, OpenText Core Software Delivery Platform opens the configuration pages defined in mode.configuration_pages.

JSON property reference

Property Purpose Notes
name Required. Unique action name for identification/logging. Keep stable once used.
title Required. Widget title shown in the gallery and on the dashboard.  
url Required. URL of the widget's main display page. Use {bundle_url} if uploading as a bundle.
mode.name Required. Must be set to "widget".  
mode.description Description shown in the widget gallery under the title. Explain what the widget visualizes.
mode.configuration_pages Array of configuration page objects, each with title and url. Each page becomes a tab in the configuration dialog.
workspaces Allow-list of workspace IDs. Cannot be used with exclude_workspaces.
exclude_workspaces Deny-list of workspace IDs. Cannot be used with workspaces.

Implement configuration pages

Configuration pages are HTML pages embedded as iframes in the widget's configuration dialog. They allow users to define the widget's data scope, filters, and display options. All configuration choices are stored in a single configuration object per widget instance, persisted by OpenText Core Software Delivery Platform.

Read configuration on page load

When a configuration page loads, request the current saved configuration from OpenText Core Software Delivery Platform to populate the UI with existing settings.

For most configuration pages, you perform the following steps:

  1. Parse panel_id from the URL.

  2. Send octane_read_widget_configuration to OpenText Core Software Delivery Platform.

  3. Listen for octane_widget_configuration and populate the UI with the received data.

Copy code
const urlParams = new URLSearchParams(window.location.search);
const panel_id = urlParams.get('panel_id');

window.parent.postMessage({
  event_name: 'octane_read_widget_configuration',
  data: { panel_id }
}, '*');

window.addEventListener('message', (e) => {
  const message = e.data;
  if (!message || message.event_name !== 'octane_widget_configuration') {
    return;
  }

  const config = message.data.config || {};
  // Populate the UI with config values.
  // On first use, config is empty — apply defaults here.
});

Persist configuration changes

When the user changes a setting, build a configuration object and send it to OpenText Core Software Delivery Platform. Send this event on every change; OpenText Core Software Delivery Platform persists the latest value.

Copy code
const config = {
  data: {
    entity_type: 'work_item',
    subtypes: ['defect'],
    filter: { /* advanced filter object */ }
  },
  display: {
    group_by: 'severity',
    time_bucket: 'week'
  }
};

window.parent.postMessage({
  event_name: 'octane_update_widget_configuration',
  data: { panel_id, config }
}, '*');

Implementation notes

  • Both configuration pages and the main widget display page are loaded in iframes.

  • Always parse panel_id from the URL and include it in every postMessage call.

  • Structure the configuration object logically (for example, separate data and display sections) to make future changes easier.

  • Configuration is stored per user — each user has their own saved state for each widget instance.

Implement the display page

The display page is the main widget rendered on the dashboard. It reads the saved configuration, fetches data from OpenText Core Software Delivery Platform REST APIs, and renders the visualization.

Read configuration and fetch data on load

Copy code
const urlParams = new URLSearchParams(window.location.search);
const panel_id = urlParams.get('panel_id');

window.parent.postMessage({
  event_name: 'octane_read_widget_configuration',
  data: { panel_id }
}, '*');

window.addEventListener('message', async (e) => {
  const message = e.data;
  if (!message || message.event_name !== 'octane_widget_configuration') {
    return;
  }

  const config = message.data.config || {};

  try {
    showLoading();
    const data = await fetchDataFromOctane(config);
    renderChart(data, config);
  } catch (err) {
    showError(err);
  } finally {
    hideLoading();
  }
});

Handle UX edge cases

  • Loading states: Indicate data is being fetched to avoid a blank widget.

  • Empty states: Show a clear message when no data matches the configured filter.

  • Error states: Display actionable messages for network or API errors.

  • Configuration updates: If the user reconfigures the widget while it is open, the display page may receive another octane_widget_configuration or a dedicated configuration-changed event. Reuse the same handler to re-fetch and re-render without a full page reload.

Advanced filtering

Most widgets require a method to scope the data — for example, "defects from Program A, Severity = Critical, last 90 days".

Scripted widgets integrate with OpenText Core Software Delivery Platform's Advanced filter UI. Widgets support multiple advanced filters within the same widget instance. Each filter is identified by a unique filter_id, which is used to differentiate between them.

From a configuration page, you can do the following:

  • Request that OpenText Core Software Delivery Platform open the Advanced filter dialog.

  • Allow the user to define or refine a filter.

  • Have OpenText Core Software Delivery Platform write the resulting filter JSON into a specific property of your configuration object.

Note: Context filters are not supported for scripted widgets.

Request the Advanced filter dialog

You send an event requesting that OpenText Core Software Delivery Platform open the Advanced filter dialog with specified scope and constraints. OpenText Core Software Delivery Platform handles displaying the dialog, limiting the entities and fields to those you specify, and returning the filter into your configuration.

Copy code
window.parent.postMessage({
  event_name: 'octane_show_filter_dialog',
  data: {
    panel_id,
    filter_id: 'myFilterId',        // unique identifier for this filter instance
    entity_type: 'work_item',
    subtypes: ['feature', 'defect'], // optional
    restrict_fields: ['team', 'program'], // optional
    allow_cross_filters: true,       // optional
    cross_filter_fields: ['program'], // optional
    bind_to_config_property: 'data.filter' // property in config to store the filter
  }
}, '*');

This request instructs OpenText Core Software Delivery Platform to:

  • Build a filter for features and defects.

  • Allow cross-filters on program.

  • Store the resulting filter JSON under config.data.filter.

React to filter changes

After the user applies a filter in the Advanced filter dialog, your configuration page should update its UI to reflect the new filter settings. When the user closes the dialog, listen for the octane_filter_dialog_closed event and retrieve the data.filter object, which contains the filter selected by the user. Update your configuration accordingly.

Copy code
window.addEventListener('message', (e) => {
  const message = e.data;
  if (!message || message.event_name !== 'octane_filter_dialog_closed') {
    return;
  }
  const filter = message.data.filter;
  // Update config with the new filter and re-render.
});

Convert a filter to a REST query string

Scripted widgets can convert filter objects to query strings for use in REST API calls. This is accomplished through a dedicated request/response event pair.

Request event: octane_convert_filter_to_query

Sent from the widget to OpenText Core Software Delivery Platform to request conversion of a filter to a query string:

Copy code
window.parent.postMessage({
  event_name: 'octane_convert_filter_to_query',
  workspace: params.workspace,
  shared_space: params.shared_space,
  data: {
    panel_id,
    filter_id: 'myFilterId',
    filter: filter,
    entity_type: 'work_item'
  }
}, '*');

Response event: octane_filter_converted_to_query

Sent from OpenText Core Software Delivery Platform to the widget as a response, providing the converted query string:

Copy code
window.addEventListener('message', (e) => {
  const message = e.data;
  if (!message || message.event_name !== 'octane_filter_converted_to_query') {
    return;
  }
  const query = message.data.query;
  // Use query in a REST API call.
});

Color consistency

To ensure your visualizations appear native to OpenText Core Software Delivery Platform, the chart series or categories can use the same colors as OpenText Core Software Delivery Platform's UI. Scripted widgets can request item colors for a given list of entity types and their corresponding entity IDs (for example, { entity_type: 'metaphase', entity_id: 'metaphase.work_item.new' }). These colors are then applied to charts, boards, or any other visualization.

Requesting colors for a given list of entity types and their corresponding IDs

In a case where your trend widget displays a stacked bar chart, you can send a request to retrieve colors for specific entities:

Copy code
window.parent.postMessage({
  event_name: 'octane_get_entity_color',
  data: {
    panel_id,
    entities: [
      { entity_type: 'metaphase', entity_id: 'metaphase.work_item.new' },
      { entity_type: 'metaphase', entity_id: 'metaphase.work_item.intesting' },
      { entity_type: 'list_node', entity_id: 'list_node.test_level.unit' }
    ]
  }
}, '*');

window.addEventListener('message', (e) => {
  const message = e.data;
  if (!message || message.event_name !== 'octane_entity_color') {
    return;
  }
  message.data.entities.forEach(item => {
    const colorHex = item.color.hex; // for example: #1fad2dff
    // Apply to chart series or legend.
  });
});

Drill-down

You can make dashboards more useful by enabling users to drill down. Scripted widgets can open an entity list dialog with a specific filter and optional column layout using the octane_display_entity_list event. You can trigger this from a click handler — for example, when the user clicks a bar in your trend chart.

Drill-down event structure

This event allows users to navigate from a high-level visualization to a detailed list of entities that comprise a data point. The drill-down message structure is as follows:

Field Details
entity_type / subtypes Base entity type (for example, work_item) or specific subtypes (for example, defect).
title Optional dialog title.
description Optional description shown as inline text.
query REST query string that filters the entities displayed.
visible_columns Optional list of field names to show as columns.

The following example demonstrates the usage:

Copy code
window.parent.postMessage({
  event_name: 'octane_display_entity_list',
  data: {
    panel_id,
    entity_type: 'defect',
    title: 'Open critical defects',
    description: 'Filtered by Severity = Critical',
    query: '?query=' + encodeURIComponent(
      'severity EQ {list_node}^list_node.severity.critical; phase EQ {list_node}^list_node.defect_phase.new'
    ),
    visible_columns: ['id', 'name', 'severity', 'owner']
  }
}, '*');

Tip: Use {query} and related URL tokens to build robust queries when selections may exceed 500 items.

Upload and deploy the widget

After your JSON, configuration pages, and display page are ready, package them into a bundle and upload them as an external action.

Prepare the widget bundle

  1. Place your JSON configuration file at the root of a folder — for example, defect-trend-widget.json.

  2. Under that folder, organize your code (HTML, CSS, JS), for example:

    • my_external_widget/index.html

    • my_external_widget/index.js

    • my_external_widget/data_config.html

    • my_external_widget/display_config.html

  3. Create a zip archive with the JSON file at the root of the zip.

Upload in External action editor

To upload the widget bundle as a shared space admin:

  1. Open Settings > Management > External action editor.

  2. Click Upload Bundle.

  3. Choose Upload New Action and select your zip file.

  4. Click Save.

Users can add the widget from the widget gallery to their dashboards and configure its settings. Scripted widgets are included in the My Organization category.

For complete upload details, see External actions.

Sample bundle

A sample scripted widget bundle is available that demonstrates end-to-end implementation of a backlog analysis widget.

Download: external_action_widget.zip

Getting started with the sample

  1. Download the sample bundle.

  2. Review action-configuration.json to understand the JSON contract and widget properties.

  3. Examine the configuration page to observe postMessage event patterns for reading and persisting configuration.

  4. Study the widget display page to understand how data is fetched from OpenText Core Software Delivery Platform and rendered.

  5. Customize the bundle for your use case by modifying entity types, filters, and visualization logic.

Bundle structure

Copy code
backlog-widget/
├── action-configuration.json
└── external_action_widget/
    ├── index.html               # Main widget display page
    ├── index.js                 # Widget logic
    ├── index.css                # Widget styling
    └── configuration_pages/
        └── data_page/
            ├── data_page.html   # Configuration form
            ├── data_page.js     # Configuration event handling
            └── data_page.css    # Configuration styling

The sample demonstrates:

  • JSON configuration with widget identity, display title, and configuration page layout

  • Configuration page with entity type selector, Advanced filter integration, and event-driven communication

  • Display page that reads configuration, fetches data, renders a priority-based visualization, applies OpenText Core Software Delivery Platform colors, and implements drill-down

Best practices

Security

  • Validate all user input in configuration pages before using it in REST API calls.

  • Use HTTPS for all external resources.

Performance

  • Use server-side aggregation rather than fetching large raw datasets to the client.

  • Use REST API pagination for large result sets.

  • Cache data where safe to avoid redundant calls on each render.

User experience

  • Provide a clear mode.description so users understand what the widget does before adding it to a dashboard.

  • Display a loading indicator while data is being fetched.

  • Show meaningful empty states and actionable error messages.

Configuration design

  • Keep configuration pages focused and minimal.

  • Use clear labels and hints on form inputs.

  • Structure the configuration object logically (for example, separate data and display subsections) to support future changes without breaking existing saved configurations.

Testing

  • Test with different data volumes, filters, and workspace configurations.

  • Test network error and empty result scenarios.

  • Verify that drill-down, color usage, and advanced filters work together as expected.