Last updated Dec 8, 2017

Dialog - Archived

Add-ons can contribute Views in HipChat modal Dialogs. Dialogs should only be used for interrupt flows, e.g. "select and post a meme". If the content of the View is meant to provide more context in the conversation, you should use the Sidebar - Archived instead.


When to use

Modals can be used to gather critical input from the user such as integration configuration preferences. Modal dialogs are used to get a response from a user, to reveal critical information that cannot be ignored, or to show data without losing the overall context. Interactions on the main page must wait for the dialog to close.

UI Controls

  • Modals act similar to light boxes in that when opened, the HipChat client in the background is whited out.
  • Actions are executable within the application via the primary and link buttons.

Actions

  • The main action should be a primary button, other actions should be link buttons. In most cases, there should only be two actions. 

  • The 'x' button in the top right of the modal is used to close the dialog window. Note this is only visible if the dialog does not use a bottom bar and therefore does not have a 'Close' or 'Cancel' link button. 

Consider 

  • Avoid non-descript primary action labels - instead, use action verbs that by themselves describe what will happen on click (for example 'Save' instead of 'Done').

Using Dialogs

Declaring a dialog

To use a dialog, your add-on must first declare a dialog in its descriptor:

Descriptor

1
2
"capabilities": {
    ...,
    "dialog": [
        {
            "key": "myaddon-dialog",
            "title": {
                "value": "An add-on dialog title"
            },
            "url": "{{localBaseUrl}}/dialog"
        }
    ]
}

These are the minimal required properties. You can also specify options to customize the look and feel of your dialog. All of these are purely optional:

Descriptor

1
2
"capabilities": {
    ...,
    "dialog": [
        {
            "key": "myaddon-dialog",
            "title": {
                "value": "A Dialog Title"
            },
            "url": "{{localBaseUrl}}/dialog/complex",  --> The url of the dialog contents
            "options": {
                "style": "normal",      --> Can also be "warning", for an error or warning dialog
                "primaryAction": {      --> The primary action button
                    "name": {
                        "value": "Yes"
                    },
                    "key": "dialog.yes",  --> The key is needed if you want to handle the button click
                    "enabled": false      --> Is true by default, but you can set it to false if you want to enable the button dynamically
                },
                "secondaryActions": [   --> If not specified, a default "Close" button will be rendered. You can declare an empty array for no secondary actions.
                    {
                        "name": {
                            "value": "No"
                        },
                        "key": "dialog.no"  --> The key is only needed if you want to handle the button click
                    }
                ],
                "size": {               --> Alternatively, you can specify "size": "small" or "medium" or "large" or "xlarge"
                    "width": "550px",     --> You can specify pixels (px) or percent (%)
                    "height": "333px"     --> You can specify pixels (px) or percent (%)
                },
                "hint": {
                    "value": "Some hint" --> The hint shown in the lower left side of the dialog
                },
                "filter": {             --> If present, a filtering/search box will be rendered in the dialog header
                    "placeholder": {
                        "value": "Search"   --> The default placeholder value that is shown in the filter box
                    }
                }
            }
        }
    ]
}

Implementing the Dialog Content

See Dialog and Sidebar Views - Archived.

The dialog iframe content is also responsible for handling events that originate from the dialog buttons or filter box:

Handling button clicks

Mostly likely you want to perform some action once the user presses "OK" or "Save" (or selects any of the secondary actions). You can register an event handler for every button by checking for its key (the one that you specified in the descriptor)

If you specify the primary action as:

Descriptor

1
2
"primaryAction": {
  "name": {
    "value": "Yes"
  },
  "key": "dialog.yes"
},

You can then handle the click by using

Dialog

1
2
function buttonClicked(event, closeDialog) {
  if (event.action === "dialog.yes") {
    // handle the action here
  }
  closeDialog(true); // you can also pass false if you want to prevent the dialog from closing (missing required fields, for instance)
}
AP.register({
  "dialog-button-click": buttonClicked
});
Handling filter box events

 This works analogous to the button click handlers:

Dialog

1
2
function filterChanged(event) {
  // filter your results here
}
AP.register({
  "dialog-filter-changed": filterChanged
});

These events are already debounced by the dialog implementation, so you will only receive an event 250ms after the user stopped typing in the filter box.

Note: Calls to AP.register need to register for all events at once. Subsequent calls overwrite the registrations of the previous call. To listen for both button clicks and filter events, you would use:

1
2
AP.register({
  "dialog-button-click": buttonClicked,
  "dialog-filter-changed": filterChanged
});

Opening a Dialog

From a Message Action or an Input Action

Declare the message action in the descriptor: 

Descriptor

1
2
"capabilities": {
    ...,
    "action": [
        {
            "key": "myaddon-action-opendialog",
            "name": {
                "value": "Open dialog"
            },
            "target": "myaddon-dialog",           --> must match the key from the dialog
            "location": "hipchat.message.action"  --> or hipchat.input.action
        }
    ]
}

This will open a dialog, and in there an iframe in which HipChat will load the page from the URL specified in the dialog "url" field. 

You can also override the dialog options:

Descriptor

1
2
"capabilities": {
    ...,
    "action": [
        {
            "key": "message.reminder",
            "name": {
                "value": "Set Reminder"
            },
            "location": "hipchat.message.action",
            "target": {
                "key": "message.reminder.dialog",
                "options": {
                    "hint": "some other hint"
                }
            }
        }
    ]
}

In the card description or in a notification message, you can link to a dialog by using the data-target attribute:

1
2
{
  "style": "link",
  "id": "3422b9e1-ebbc-42af-8b06-5212fa8f3a01",
  "url": "http://example.com/example.png",
  "title": "Card Title",
  "description": {
    "format": "html",
    "value": "<a href='#' data-target='add-on-key:dialog-key'>Open Dialog</a>"
  },
  "date": 1448348496130
}
From another View

Using the Javascript API, you can open a Dialog View from another View (e.g. from a Sidebar View):

View

1
2
AP.require("dialog", function (dialog) {
  dialog.open({
    key: "myaddon-dialog"  // must match the key for the dialog
  })
});

You can override any options that you declared in the descriptor, by passing an additional options parameter:

View

1
2
var dialogOptions = {
  title: "My Dialog",
  options: {
    style: "warning",
    primaryAction: {
      name: "Yes",
      key: "dialog.yes",
      enabled: false
    },
    secondaryActions: [
      {
        name: "No",
        key: "dialog.no"
      }
    ],
    size: {
      width: "500px",
      height: "300px"
    },
    hint: "Some hint",
    filter: {
      placeholder: "Search..."
    }
  }
};
 
AP.require("dialog", function (dialog) {
  dialog.open({
    key: "myaddon-dialog",  // must match the key for the dialog
    options: dialogOptions
  })
});

The format of the options parameter follows the format of the descriptor, but all the localizable elements as they appear in the descriptor are flattened:

1
2
"name": {
  "value": "Yes"    --> becomes -->    "name": "Yes"
}
Passing data to the dialog

In addition to specifying the look&feel options, you can also pass your own data to the dialog when you open it:

View

1
2
AP.require("dialog", function (dialog) {
  dialog.open({
    key: "myaddon-dialog"   // must match the key for the dialog
    options: dialogOptions  // as above
    parameters: {
      foo: "bar"            // anything you want
    }
  })
});

Now the dialog iframe contents can receive your data by registering another event listener:

Dialog

1
2
function receiveParameters(parameters) {
  // This function will receive the parameters that were used in dialog.open(...)
  if (parameters.foo === "bar") {
    // do something
  }
}
AP.register({
  "receive-parameters": receiveParameters
});

Updating a dialog

Dialogs can also update themselves. A typical example is changing the primary button enablement state depending on whether a user provided enough data.

Generally, all dialog attributes (besides key and url) can be updated dynamically by using the same format that we used to open a customized dialog above:

1
2
AP.require("dialog", function(dialog) {
  dialog.update(dialogOptions);  // dialogOptions look like the ones we used above to to open a custom dialog
});
 
// For example:
AP.require("dialog", function(dialog) {
  dialog.update({
    title: "New Title",
    options: {
      size: {
        width: "700px",
        height: "500px"
      }
    }
  });
});

For the common case of updating the primary action, we provide a convenience method:

1
2
AP.require("dialog", function(dialog) {
  dialog.updatePrimaryAction({
    name: "New Label",  // optional
    enabled: false      // or true
  })
})

Closing a dialog

A dialog can also close itself by calling:

1
2
AP.require("dialog", function(dialog) {
  dialog.close();
})

Rate this page: