Share this page

# Scripted widgets

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

In this topic:

## 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 Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management within embedded iframes.

- They communicate with OpenText Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management. 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 Software Delivery Management 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 Software Delivery Management | Request the current saved configuration for this widget instance. Send this when a configuration page or the display page loads.
`octane_widget_configuration` | OpenText Software Delivery Management → Widget | OpenText Software Delivery Management responds with the saved configuration. On first use, the configuration object is empty.
`octane_update_widget_configuration` | Widget → OpenText Software Delivery Management | 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 Software Delivery Management | Request that OpenText Software Delivery Management open the Advanced filter dialog for the specified entity type and scope.
`octane_filter_dialog_closed` | OpenText Software Delivery Management → Widget | OpenText Software Delivery Management notifies the widget that the user closed the filter dialog and provides the resulting filter object.
`octane_convert_filter_to_query` | Widget → OpenText Software Delivery Management | Request conversion of a filter object to a REST query string for use in API calls.
`octane_filter_converted_to_query` | OpenText Software Delivery Management → Widget | OpenText Software Delivery Management responds with the converted query string.

#### Color and navigation events

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

#### Standard events

Event | Direction | Purpose
--- | --- | ---
`octane_refresh_entity` | Widget → OpenText Software Delivery Management | Instruct OpenText Software Delivery Management to refresh specified entities.
`octane_display_entity` | Widget → OpenText Software Delivery Management | Instruct OpenText Software Delivery Management to open the details of a specified entity.
`octane_report_error` | Widget → OpenText Software Delivery Management | Add an entry to the OpenText Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management.

### Read configuration on page load

When a configuration page loads, request the current saved configuration from OpenText Software Delivery Management 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 Software Delivery Management.

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 Software Delivery Management. Send this event on every change; OpenText Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management'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 Software Delivery Management open the Advanced filter dialog.

- Allow the user to define or refine a filter.

- Have OpenText Software Delivery Management 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 Software Delivery Management open the Advanced filter dialog with specified scope and constraints. OpenText Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management 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 Software Delivery Management, the chart series or categories can use the same colors as OpenText Software Delivery Management'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 Software Delivery Management 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 Software Delivery Management 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.

https://admhelp.microfocus.com Software Delivery Management 8942
Tips to improve your search results... OpenText™ Software Delivery Management Help Center OpenText™ Software Delivery Management Help 26.3 All View Local Help Center View Online Help Center Version: