Customize search results in Home

You can create a custom preview pane for search results that are displayed in Home using custom templates. You can define a a variety of responses to a search result based on which fragment of the pane is clicked.

Similar to notification templates

Custom search result templates are much like custom notification templates.

Parts of a template

The components of a template are called “fragments,” which are assembled in a JSON object to create the template that will be rendered.

The set of fragments is similar to that for notification templates:

  • container groups other fragments, with no visible parts of its own

  • text displays text

  • list displays a list of name-value pairs

  • image displays an image

  • button (unique to search result templates) executes an action

Button fragment

The button fragment enables the user to do more than merely select a search result. The user can interact with the result in whatever way makes sense for your use case. For example, a View Client button would take a different action than a View Instrument button.

Actions

The button, image, and text fragments process mouse clicks. Each of these fragments returns an optional action string, which you can use to direct your code. A click on an image could return the string ‘Zoom Image’, which could be used to display a full-size view of that image. A click on the text of a client’s name could return the string ‘Call Contact’, which could be used to start a phone call to that client.

Example of using an action:

const myComposition: TemplateFragment = {
  ... // Fragment properties
  action: 'Call Contact';
}

// CLIProvider.onResultDispatch()
async function onResultDispatch(res: CLIDispatchedSearchResult) {
  if (res.action.name === 'Call Contact') {
    services.phone.call(res.data.phoneNumber);
  }
}

How it works

Essentially, you create a JSON object to describe how to render your search results. That JSON object contains the fragments you need to display a search result, along with some style information for each fragment.

When the user clicks on a search result, Home renders the data according to your layout and displays that on screen.

645

Screenshot of a search result that uses a custom template with two text strings, images, two buttons, and a list.

How to do it

Leveraging custom templates for search results requires two phases:

  1. Define the custom template JSON object. This JSON object drives a large part of the custom search results.
  2. When generating search results, apply the template to the data for each search result.

Templates and fragments

The custom template can be thought of like a simplified HTML render, where the fragments are like simplified HTML elements.

FragmentCorresponds to
container<div>
text<span>
list<ul> with no list marker, and with name-value pairs
image<img>
button<button> but with more complex formatting possible

Two fragments have an optional children property; container and button. Just as HTML pages often have a <div> within a <div> within a <div>, custom templates can have a container within a container within a container. The children property enables this.

Because buttons often require flexible formatting, the button fragment can contain children, too. For example, a button can have text and an image fragment. And a container to hold them both. And a bunch more fragments if you really want them.

Styles

To continue the analogy with HTML, you can define styling for template fragments, via the style element. The style element can contain React inline style properties such as flex properties, colors, sizes, and fonts. Remember that React style properties use “camelCase” names, such as backgroundColor rather than “kebab-case” names, such as background-color, as in plain CSS.

📘

Do not use position:fixed

The position:fixed CSS property value is not allowed, to prevent elements from appearing outside their search result card; the create() function throws an error if it is used.

Optional fragments

Fragments can be optional. Why is this useful?

Not every fragment that you define is needed for every search result that the template containing it might be applied to. In some cases, the corresponding data might not exist, but the result of the search result is valid.

Data items related to a search result are mapped via the fragment's dataKey property. If a fragment's optional property is true and a value matching its dataKey property is not supplied for a search result, that fragment is not rendered. If optional is false or not defined, then missing data in a search result causes an error for that search result.

Put it together

Create the JSON object template. In this case, myComposition:

const myComposition: TemplateFragment = {
  type: 'container',
  style: {
    display: 'flex',
    flexFlow: 'column',
    // some more styling
  },
  children: [{
    type: 'text',
    style: {
      color: 'red',
      // ...
    }
    dataKey: 'description', // a key to templateData
  },
  {
    type: 'image',
    style: {
      width: '200px',
      // ...
    }
    dataKey: 'imageUrl',
  }]
}

This code creates a composition that looks like this:

450

Diagram of a template consisting of a container enclosing a text fragment and an image fragment

But there is a very important question that hasn’t been answered yet. How do you take the data you found in your search and put it on the rendered display?

You use the dataKey property of the fragment.

To illustrate, let’s say that, as in the template JSON object shown above, myComposition, you have a text fragment of red styled text where dataKey is set to description, and an image fragment where dataKey is set to imageUrl. Let’s further say your search results object also has properties named description and imageUrl.

And in your search result data you have a search result where the description property is set to the text “Company 1” and imageUrl points to an image file of the Company 1’s logo. For example, the first element of mySearchResults as seen here:

const mySearchResults: HomeSearchResult[] = [{
  ... // standard home search result options
  template: CLITemplate.Custom,
  templateContent: {
    layout: myComposition,
    data: {
      description: 'Company 1',
      imageUrl: 'https://company1.com/logo.png'
    }
  }
},
{
  ... // standard home search result options
  template: CLITemplate.Custom,
  templateContent: {
    layout: myComposition,
    data: {
      description: 'Company 2',
      imageUrl: 'https://company2.com/logo.png'
    }
  }
}];

When the search result is rendered by the custom template, the text 'Company 1' appears as red text in the rendered display, and Company 1’s logo appears below the red text. Likewise, the name and logo for Company 2 are displayed for the search result for Company 2.