Options
All
  • Public
  • Public/Protected
  • All
Menu

Cards

Cards provide a flexible and customizable user experience in the Relativity Review application. You can use them to arrange standard and custom content in the Relativity Review Interface. The following UI elements are examples of cards:

  • Coding pane
  • Persistent highlight sets (PHS) pane
  • Markup sets navigator
  • Individual viewers including the native, image, text, and production viewers

This page contains the following information:

Cards

A card is a UI component that has a title, an optional icon, and content. The content is the body of the card. You can create a custom card in various locations in the UI. Some examples of card contents include:

  • An IFrame that loads an external HTML page and its assets, such as CSS and JavaScript files
  • Custom HTML content

Use cards to extend the existing functionality of Relativity Review by adding custom content. For example, create a custom card that has form fields to accept user inputs and buttons that trigger actions based on user requirements.

Define a config object for a card

The Relativity Review API includes the card framework that you use to define and create cards. For the API reference, see IReviewInterfaceApi.cards and ICardService.

Before creating a card, you need to define properties for it by creating a card configuration object. See ICardConfig for a list of required and optional properties that you can set on this object.

The following code sample illustrates a sample card configuration with these features:

  • The config object defines an IFrame card that loads an external page. When this card is created, it's placed at the location specified by the location property. In this example, the location is the right side of the document viewer. For available locations, see Card locations.

  • Depending on the card location in the UI, the icon or the card title may be display on the card tab. For example, if multiple cards were in the same location, several card icons could be positioned below each other. You can specify the card icon as a CSS classname, an image URL, or a resource file name. You must define these parameters. For more information, see ICardIcon.

var testCardConfig = {
  id: "test-card",
  title: "Test Card",
  icon: {
    url: "https://www.relativity.com/relativity/images/footer-logo-2x.png",
  },
  singleton: true,
  location: {
    layoutId: "review",
    paneId: "ri-review-right-accordion",
    dockIndex: 0,
  },
  loader: {
    iframe: {
      url: "https://www.relativity.com",
    },
  },
};

Create a card

After you create a config object, complete the following steps to create a card:

  1. Use ICardService.defineCard function to define the card. This function takes one or more ICardConfig objects as arguments.

  2. Use ICardService.createCard function to create the card. This function takes the card ID as a required parameter.

Create cards in response to the ApiReady event

In general, define and create a card in response to the apiready (ReviewInterfaceEventType.ApiReady) event in the Review Interface lifecycle as follows:

reviewApi.on("apiready", function() {
    // 1. Create card config object.
    var testCardConfig = ...

    // 2. Define card.
    reviewApi.cards.defineCard(testCardConfig);

    // 3. Create card.
    var card = reviewApi.cards.createCard(testCardConfig.id)
});

See the Review API documentation for information about accessing the APIs, invoking them, and listening to lifecycle events.

Create a card on the fly

You can use the ReviewApi to create a card on the fly. When the Review Interface is loaded, the ReviewApi is available for use directly in the browser console window for defining and creating cards:

var testCardConfig = ...;

window.review.cards.defineCard(testCardConfig);

var card = window.review.cards.createCard(testCardConfig.id);

Optionally pass additional arguments during card creation

You can use an optional third parameter to pass additional arguments to the ICardService.createCard function. In this way, the arguments are available later in the lifecycle event handlers for a card. See ICard.instance for lifecycle events that you can subscribe to when creating a card in the ICardConfig.createInstance function.

var myCard = reviewapi.cards.createCard(
  "test-card-id",
  undefined,
  "my-parameter",
  3,
  null
);

myCard.parameters[0] > "my-parameter";

myCard.parameters[1] > 3;

myCard.parameters[2] > null;

Card loaders

You can create the following types of cards:

When defining a card, set the ICardConfig.loader property to a type of card loader.

IFrame cards

IFrame cards use an HTML <iframe> to render contents of card from a URL as follows:

reviewapi.cards.defineCard({
    ...
    loader: {
        iframe: {
            url: "https://www.relativity.com"
        }
    }
});

Access the card object from within the IFrame

You can access the card object from within the IFrame using the following JavaScript:

const card = window.frameElement.reviewCard;

Auto resolve

The auto resolve feature controls how the card framework identifies an IFrame card. By default, the card framework recognizes an IFrame card as loaded after the onload event for <iframe> element fires.

Under certain circumstances, the onload event may fire before the contents of your card are ready to be displayed to the user. For example, this situation may occur when an IFrame card loads a web page containing a single page application (SPA). The SPA may call REST APIs at startup to retrieve data for rendering in the DOM.

You can disable the auto resolve feature to prevent the card framework from considering the card content loaded until the data is retrieved and rendered. You can add code to your card to notify the card framework when the contents of the card have been fully loaded or when it fails to load.

To disable the auto resolve feature, set the optional autoResolveLoad property of the loader configuration for your card to false:

reviewapi.cards.defineCard({
    ...
    loader: {
        iframe: {
            url: "https://www.relativity.com",
            autoResolveLoad: false
        }
    }
});

Next, add code to your application to notify the card framework when the card is fully loaded by invoking the ICard.completeFrameLoad method on the card:

const card = window.frameElement.reviewCard;
card.completeFrameLoad();

You should also add code to your application to notify the card framework when the card fails to load. Do this by invoking the ICard.failFrameLoad method on the card. The message on the Error object passed to the failFrameLoad() function is displayed to the user. See the following code sample:

const card = window.frameElement.reviewCard;
card.failFrameLoad(new Error("Error message will be displayed to user."));

When auto resolve is disabled, the card framework displays a card skeleton instead of your card until the card invokes one of the frame load methods.

Review these special considerations for using IFrame cards when the HTML page inside the IFrame is loaded from a different domain other than the Relativity hosted domain:

  • Keyboard shortcuts inside of IFrame cards don't work in all supported browsers.

  • The resizing of cards in Internet Explorer might not work as expected.

  • Access to the card object isn't possible. Don't disable auto resolve in these cases.

Custom cards

You can use custom cards to add HTML content directly to the DOM. Custom cards declare the loadCard() and unloadCard() functions that are called by the card system:

  • The loadCard() function should return a promise. It should also resolve when the loadCard() function has completed rendering the card contents into the specified target element.
  • The unloadCard() function returns a promise that is resolved when it completes unloading the card contents.

To define a custom card, include the loader.custom property in your card configuration. See ICustomCardLoader for the API reference.

reviewapi.cards.defineCard({
    ...
    loader: {
        custom: {
            loadCard: function(card, target) {
                target.innerHTML = "<h1>Hello, this is my custom card</h1>";
                return Promise.resolve();
            },
            unloadCard: function(card, target) {
                target.innerHTML = "";
                return Promise.resolve();
            }
        }
    }
});

Note: Custom cards don't support the auto resolve feature. You control the implementation of custom cards so auto resolving card loads isn't necessary.

Communication between cards

You can create a custom function that enables communication between two cards. This function is called by another card as part of an object that is returned by createInstance function (ICardConfig.createInstance) of ICardConfig object. See the following sample code:

// Define card configuration.
var testCardConfig = {
    id: "test-card",
    title: "Test Card",
    createInstance: function(card) {
        // Create an object that contains a custom function that the other card calls.
        return {
            cardLoaded: function(api, card) {
                ...
            },
            customFunction: function(data) {
                ....
            }
        };
    }
};

// Create card.
reviewapi.cards.defineCard(testCardConfig);
var card = reviewapi.cards.createCard(testCardConfig.id);

The following code sample illustrates how the card is retrieved by its ID and how it invokes the customFunction() defined in ICardConfig.createInstance function. You can make the getCard() and customFunction() calls from a different location other than one used for the card definition and creation.

// Get the card object and invoke a function on the card's instance.
//The following lines of code can be used as part of another card.
var testCard = reviewapi.cards.getCard("test-card");

testCard.instance.customFunction({});

Card skeleton

The card system includes a loading skeleton that is displayed by default while cards are loading. Card loading occurs immediately before the card contents are displayed.

Defining card skeleton

Use the skeleton property of ICardConfig to replace the default skeleton with a custom skeleton. See ICardSkeleton for a list of properties and functions.

reviewapi.cards.defineCard({
    id: "card-1"
    ...
    skeleton: {
        delay: 500,
        minimum: 250,
        showSkeleton: function(card, target) {
            target.innerHTML = "Please wait, loading content...";
        },
        hideSkeleton: function(card, target) {
            target.innerHTML = "";
        }
    }
});

Showing card skeleton explicitly

Use the showSkeleton (ICard.showSkeleton) function of the ICard interface to explicitly show the card skeleton. If the skeleton property wasn't set as part of the card definition, a default loading skeleton is displayed:

var card = reviewapi.cards.getCard("card-1");

card.showSkeleton(true);

Hiding card skeleton explicitly

Use the hideSkeleton (ICard.hideSkeleton) function to explicitly hide the card skeleton:

var card = reviewapi.cards.getCard("card-1");

card.hideSkeleton(true);

Report errors

You can use the card system to report errors that occur during the lifetime of a card. You can also display an error message on a card when a fatal or non-recoverable error occurs.

Call the ICard.reportError function on the ICard object from any card instance (ICardInstance) lifecycle function.

The following code sample uses this process for reporting errors:

  • It defines a card config for a custom card with a button control and attaches a click event handler to it.
  • The button click event handler then calls the ICard.reportError function to simulate an error in the card.
  • The ICard.reportError requires two arguments: an error message displayed on the card in case of fatal errors, and an ICardError object.

When ICard.reportError is called, the following actions occur if the isFatal property of ICardError is true:

var cardConfig = {
  id: "card-error-state-test",
  title: "Test Card Error State",
  location: { layoutId: "review", paneId: "ri-review-right-accordion" },
  loader: {
    custom: {
      loadCard: function (card, target) {
        let html =
          "<div class='testButtonControls' style='text-align: center; padding: 20%;'>";
        html +=
          "<button style='margin: 10px; height: 25px; width: 80px;' id='btnReportError'>Report Error</button>";
        html += "</div>";

        target.innerHTML = html;
        return Promise.resolve();
      },
      unloadCard: function (card, target) {
        return Promise.resolve();
      },
    },
  },
  createInstance: function (card) {
    return {
      // cardLoaded is called after the card content has loaded.
      cardLoaded: function (api, card, target) {
        var btnReportError = document.getElementById("btnReportError");
        btnReportError.addEventListener("click", function () {
          try {
            // .. do some processing ..
          } catch (e) {
            // Handle the exception here and call card.reportError().
            var cardError = {
              category: "applicationexception",
              isFatal: true,
            };
            card.reportError("An error has occurred", cardError);
          }
        });
      },
    };
  },
};

Set card states

A card can have one of the following states, which indicate its loading status:

  • Uninitialized - the initial state of a card before the Card Manager is initialized.
  • Loading - the card is loading.
  • Loaded - the card is loaded.
  • Failed - the card failed to load or crashed after loading.
  • Unloading - the card was unloaded.
  • Unloaded - the card is initialized but not loaded.

You may need to explicitly set the state of a card. For example, if an error occurs in a card, you might need to set the status to CardStatus.Failed to indicate the error state.

To set the status, pass a CardStatus enum value to the ICard.setStatus function. The text on the tab for a card displays as follows:

  • When the card status is CardStatus.Failed, the text color is red, indicating an error state.
  • When the card status has a state other than CardStatus.Failed, the text is displayed in the normal style for the tab.

Synchronize with the viewer

You can load any type of IQueueItem in the Review Interface. Make sure that you handle the type of item loaded in the viewer. For example, if your card is for an IDocumentItem, turn it off when an IRDOFileItem is loaded in the viewer.

The IViewerCollection emits an ViewerCollectionEventType.ContentChanged event when the content in the viewer changes. This event has IContentChangedEvent object that contains information about the item loaded. It includes information about the IQueueItem that was loaded and the ViewerType where it was loaded.

Use the following workflow to synchronize the content in the viewer. First, create a card:

var myCard = reviewapi.cards.createCard(config);

Next, create a handler for the IContentChangedEvent event that references your card:

function myHandler(event) {
  // This simple example enables or disables the card based on whether it's a document item.
  myCard.visible = event.type === "document";
}

Finally, subscribe to the ViewerCollectionEventType.ContentChanged event:

reviewapi.viewer.mainCollection.on("contentchanged", myHandler);

Unsubscribe from the event when appropriate for your card:

reviewapi.viewer.mainCollection.off("contentchanged", myHandler);