Relativity Lists Interaction APIs

Summary

Relativity Lists provides a robust offering of APIs to allow developers to write custom code to extend the functionality of their list pages. By creating an interaction script, developers can subscribe to events, usually beginning with an on prefix (e.g. onItemListPopulated), or access additional service APIs that allow a variety of functionality to be executed, including custom widget registration, custom modal creation, and data source overrides.

The APIs are arranged into objects based on their function. You can start by looking at the 2 top level objects in which all APIs and properties are contained:

Interaction Script Format

Relativity.Lists allows extension developers to use any valid JavaScript, and developers can assume that the following objects are available at global scope:

  • interactionApi
  • convenienceApi

Example Interaction Script

Copy
// The developer can sign up for event based callbacks
interactionApi.config.onConfigLoaded(initialize);
// The developer can declare functions to handle callbacks and make additional API calls
function initialize() {
    interactionApi.widget.registerWidgetType(...);
}

For more examples, see the Common Migration Scenarios below.

Execution Context

Relativity.Lists creates a function using the text of the script and executes it like so:

Copy
new Function(
    "interactionApi", "convenienceApi"
    `
    "use strict";
    // script text
    `
)(interactionApi, convenienceApi);

Associating With An Object Type

To associate an interaction script to be run on the Relativity List page for a specific object type, the file needs to be included in a List Page Interaction Event Handler.

The javascript file name must begin with lists.ext. to be executed on the Relativity Lists page.

Migrating a Classic List Page Interaction Script to Relativity.Lists

Existing classic List Page interaction scripts associated to an object type by a Classic List Page Interaction Script are now deprecated and will no longer be supported in the future (see the Classic List Page Deprecation Notice).

Testing A Migrated Interaction Script

Until the classic List Page is no longer supported, object types with associated classic List Page interaction scripts will continue to load the classic List Page. Once a new Relativity Lists interaction script (with a javascript file name beginning with lists.ext.) is associated to that object type, the new Relativity Lists page will automatically be used instead to execute the new interaction script.

For testing purposes, an administrator can manually enable/disable loading the new Relativity Lists page for an object type on the Relativity Lists Toggle tab. Here are the steps to complete this:

  1. Navigate to the Relativity Lists Toggle tab
  2. Either edit an existing entry, or create a new entry by clicking the New Relativity Lists Toggle button
  3. Specify the name of the object type
  4. Specify whether or not to load the new Relativity Lists page on the Use Relativity Lists field
  5. Save the entry
  6. Reload the object type's list page - the user may need to log out and back in to see the change

To migrate an existing classic List Page interaction script to use the new Relativity Lists API, refer to the information below to determine the new API to use.

Initializer API

Relativity Lists doesn't require packaging interaction scripts as a module or an IFFE. These scripts are standard JavaScript. You can also assume that the Interaction API and Convenience API exist in the global scope. Previously, interaction scripts were required to be in a RequireJS module that returned an initializer function, which executed in way that passed the object containing the service APIs to an interaction script.

Event-based APIs

Classic API Name Classic Event Relativity Lists API Notes
Page Navigation API listPageChange (pageNavigationAPI) No longer supported
New Item Button API newItemButtonInit (buttonAPI) ToolbarApi (see modifyToolbarContent)
Cell Formatter API cellFormattersInit (cellFormatterApi) DataSourceApi (see registerColumnFormatter) This API is no longer event-based. The cell formatters should be registered when the interaction script is initialized.
Data Source Override API dataSourceInit (dataSourceAPI) DataSourceApi (see registerDataSource) This API is no longer event-based. The data source override should be registered when the interaction script is initialized.
View API viewChange (viewChangeAPI) ItemListApi (see onViewChange)
Widget Menu Customization API widgetMenuCreate (widgetMenuAPI) WidgetApi (see onWidgetMenuCreate)

General service APIs

Classic API Name Classic Service Name Relativity Lists API Notes
Promise API api.promise No longer supported All modern browsers compatible with Relativity support promises.
Events API api.eventService No longer supported
Kepler Provider API api.keplerProviderService ConvenienceApi.RelativityHttpClient
Common Utilities API api.commonUtilities ConvenienceApi.RelativityHttpClient "commonConstantsAndObjects" is no longer provided
Modal API api.modalService ModalApi
Toolbar API api.toolbarService ToolbarApi
CustomWidget API api.customWidgetService WidgetApi

Common Migration Scenarios

Cell Formatter → Register Column Formatter

Classic Example
Copy
formatterApi.setFormatter("columnName", function (defaultValue, columnOptions, rowData, api) {
    // Modify or replace the return value with what is to be shown in the cell
    return api.getCellData(defaultValue);
}

Relativity Lists Example
Copy
interactionApi.dataSource.registerColumnFormatter({name: "columnName"}, {
    format: (_content: any, dataItem: FormatterDataItem) => {
        // Modify or replace the return value with what is to be shown in the cell
        return _content;
    },
    formatCsv: (_content: any, dataItem: FormatterDataItem) => { }
});

Data Source Override

Classic Example
Copy
overrideApi.setItemListDataSource(function(itemListDataSourceParams) {
    return {
            getData: {
                method: function(payload) {
                // ...
                }
                },
                inboundTransformer: {
                method: function(data) {
                // ...
                }
                },
                outboundTransformer: {
                method: function(data) {
                // ...
                }
            }
    };
});

Relativity Lists Example
Copy
interactionApi.dataSource.registerDataSource("ItemListWidget", (workspaceID: number, artifactTypeID: number) => {
    return {
            getData(activeArtifactID: number | undefined, start: number, pageLength: number, filters: Filter, sorts: dataManagement.itemList.Sort[], fieldIDs: number[], fieldCollection?: ItemListFieldCollection, activeView?: View,
            sampleState?: SampleState, browserState?: BrowserPanelState, relationalFieldId?: number, activeSavedSearchID?: number): Promise<void | ObjectManagerData> {
            // ...
        },
        transformInbound(data: any, columns: any[], workspaceID: number, callbacks: ItemListDataSourceCallbacks): ItemListData {
            // ...
        },
        transformMultiReflectedFields(fields: dataManagement.itemList.Data[], fieldCollection: ItemListFieldCollection): void {
            // ...
        }
    }
});

New Item Button API → Toolbar API

Classic Example
Copy
function() {
    function sampleHandler(api) {
        // Illustrates a function that handles page change events. It takes the Page Navigation API as a parameter.
        function handleNewItemButtonCustomizeEvent(buttonApi) {
        buttonApi.setButton({
            text: "Open my custom modal",
            eventName: "show_my_modal"
        });
    }
    return {
        // Subscribe to the "newItemButtonInit" event.
        newItemButtonInit: handleNewItemButtonCustomizeEvent
        };
    }
    return sampleHandler;
}

Relativity Lists Example
Copy
const newButton = document.createElement("button");
    newButton.innerText = "Open my custom modal";
    newButton.classList.add("rwa-button");
    newButton.addEventListener("click", async () => {
        // Open a modal
    });

    interactionApi.toolbar.v1.modifyToolbarContent("header", "_default", (_api: any, content: ToolbarContent): ToolbarContent => {
    if (content.left) {
        content.left.splice(2, 1); // Remove original button
        content.left.splice(2, 0, newButton); // Add new button with custom action
    } else {
        content.left = [newButton];
    }
    return content;
});

An object rule can also be used to override the New Item Button's URL. See New Button Override for more information.

Create Widget Type

Copy
const WIDGET_TYPE_NAME = "Widget Type Name";
interactionApi.widget.registerWidgetType(WIDGET_TYPE_NAME, (state: WidgetStateInfo) => {
    // Perform any setup needed (open modal to gather preferences, etc)
    // Update the passed stateData object with any state information to save
    const WIDGET_TITLE = "Widget Title";
    stateData.settings.title = WIDGET_TITLE;

    // Build HTML element to display in widget
    const widgetRef: HTMLElement = new HTMLElement();

    // Build widget menu items
    const menuItems: WidgetMenu[] = [{
        title: "Properties",
        visible: true,
        items: [{
                name: "Edit Widget",
                visible: true,
                type: "link",
                icon: "edit",
                onClick: () => {
                    /** Open modal to edit the widget */ }
            },
            {
                name: "Export Widget",
                visible: true,
                type: "link",
                icon: "share",
                onClick: () => {
                    /** Export widget information */ }
            }
        ]
    }];

    // Update/save the state information
    interactionApi.widget.addOrUpdateWidget(WIDGET_TYPE_NAME, stateData, stateData.settings.id);

    // Return WidgetInstance object
    return {
        wgtRef: widgetRef,
        menu: menuItems,
        title: WIDGET_TITLE
    }
    as WidgetInstance;
});

Script Caching

Relativity Lists caches all extension scripts in an IndexedDB with the path relativityLists > scripts. When a new version of a script is identified, it will pull the new version from across the network and update the cache with the new script. This cache is keyed by the version of the application that is associated with the extension script, so developers will need to be sure to update their application version when deploying updates to their extension scripts.

For manual dropping or re-adding of extension scripts through Resource Files, the updates will not take effect as the cache will not identify that a new script needs to be pulled since the version number remains the same. To workaround this issue developers can:

Modules