Skip to main content

Guide Runtime

What & Why

Needing to use custom code to achieve that specialized experience desired for a specific guide, or just to accommodate an awkward App that has special requirements, is something that will always be necessary. Today, the custom code feature has minimal support behind it with really only the objects for the active guide and step being provided.

The goal with the Guide Runtime is to provide an enhanced toolbox for doing the things that are already being done; but now, they'll hopefully be done more easily, with less boilerplate code, and more safely as the Runtime will guarantee exception handling and improve ergonomics.

Guide Lifecycle

The Guide's Lifecycle includes all aspects of a Guide's existence from the time it is added to the DOM (also known as mounted). In addition to the normal events relevant for any UI added to a web page there are also Pendo Guide specific events. Vue lifecycle events were consulted as a reference for a starting point.

List of events:

  • beforeUpdate position of guide is about to move
  • updated position of guide has moved
  • beforeUnmount about to remove guide from dom (including property with reason for removal ie Advance, Hide, or Dismiss)
  • unmounted guide removed from dom (including property with reason for removal ie Advance, Hide, or Dismiss)
  • beforeMount called before guide has been displayed (only available through Global Scripts declaration)
  • beforeAdvance called before guide has been Advanced
  • advanced guide was unmounted due to being Advanced
  • beforeSnooze called before guide has been Snoozed
  • snoozed guide was unmounted due to be snoozed
  • beforeDismiss called before guide has been Dismissed
  • dismissed guide was unmounted due to being Dismissed
  • beforePrevious called before guide has been "Advanced" to Previous Step
  • previous guide was unmounted due to be "Advanced" to Previous Step
  • hidden guide was unmounted due to being Hidden
note

JavaScript is running after it's included inside the Guide so it can't see the events preceding its existence like beforeCreate and created.

Documentation

Guide Runtime is made available as the context for the function in which all code block JS is run; it is accessible via the this keyword.

Runtime Events:

  • on

Function that takes a String representing an event (listed below) and a callback function to be called when that event occurs. The update and unmount events (include the before* variety) are generic and called regardless of context as to why they occurred. The unmount events will include a reason field to provide that context. In addition, synthetic events for each reason will be generated to allow for less boilerplate coding when needing to only respond to specific scenarios in code.

Supported Events:

  • beforeUpdate
  • updated
  • beforeUnmount
  • unmounted

Synthetic events generated to simplify listening for a specific type or unmount

  • advanced
  • snoozed
  • dismissed
  • hidden

Event handlers will receive an Event argument. The Event argument will have a structure like:

  • type - The type (from list above) of the event.
  • reason - If type is unmounted or beforeUnmount then this will show what the UX reason for the unmount is or will be. Reasons are: advanced, snoozed, dismissed, or hidden.
  • step - The step that is currently being evented upon.

Examples

The Runtime will be accessible inside the code function block by using the implicit this variable. A full description of the available API will be provided but for some potential examples a snippet might appear like:

this.on('beforeUnmount', evt => {
// this will fire before any event where the guide is removed
if (evt.unmountReason === 'advance') {
}
});
this.on('advanced', evt => {
// Do something here
});
this.on('dismissed', () => {});
// CodeBlock Function<GuideRuntime> - GuideRuntime is available in the this variable inside the function

this.on('advanced', () => {
alert('You are done!');
});

this.on('dismissed', () => {});

Global Scripts

Guide suites will often use the same code blocks over and over again to achieve a similar presentation across all guides. This is risky as it requires many guides to be edited manually and kept in sync.

To make this process easier and more predictable, the Pendo agent can be initialized with custom code blocks that run on every step. These are defined in a globalScripts list with the following format:

Some examples:

pendo.initialize({
visitor: { id: 12345 },
account: { id: 789 },
guides: {
globalScripts: [
{
script: function (step, guide) {
console.log('Step ', step.id, ' has run');
},
// Only run this on a specific known step id
test: function (step, guide) {
return step.id === 'lCQ_9DAgLJlfA6nibXYmUf-HucE';
}
},
{
script: function (step, guide) {
console.log('Step is a second step');
},
// Run on the second step of any guide
test: function (step, guide) {
return (
pendo._.findIndex(
guide.steps,
s => s.id === step.id
) === 1
);
}
},
{
script: function (step, guide) {
console.log('This guide has a badge');
},
// Run on all steps for guides that have a badge launch method
test: function (step, guide) {
return guide.launchMethod.includes('badge');
}
},
{
script: function (step, guide) {
console.log('This step matches the naming criteria');
},
// Run on all steps where the guide name or step name has "- Delay Script" in it
regex: /- Delay Script/
}
]
}
});
  • globalScripts - an array of objects that each define a custom global script
  • script - (required) custom function goes here
  • test - (optional) test function gets the step and guide object as parameters and must return a boolean value that determines whether the script runs. Ex: only run this script on the first step of every guide
  • regex - (optional) regular expression compared against the step and guide name, running the script on any matches. Ex: a regex that matches common string among a suite of guides
  • For convenience, any supported event listed above may also be provided as a function. For example:
pendo.initialize({
guides: {
globalScripts: [
{
beforeMount: function () {
console.log('This runs before the guide displays');
},
script: function () {
console.log('This runs after the guide displays');
},
unmounted: function () {
console.log('This runs when the guide is hidden');
}
}
]
}
});
note

Global scripts will run after scripts defined in custom code blocks on the step itself via Designer

Guide Local Storage

Persisted data that is able to be accessed across repeated guide step views or even be available between steps in the same multi-step guide is already a technique being used but often doesn't safely do so as local storage can be tricky based on per user settings or browser capabilities.

This api will provide a way for the data to be safely stored and have its visibility scoped to ensure each guide can trust the values it requests.

Only accessible for the current Step

this.write('storageKey', 100); // takes key string and value and safely handles local storage
this.read('storageKey'); // returns value (100) from reading "storageKey"
this.clear('storageKey'); // clears value for "storageKey"

Accessible across Steps of the same Guide

this.writeByGuide('key', 200); // appends current guide ID to the key string and stores value
this.readByGuide('key'); // returns value (200) from reading "key" on the current guide
this.clearByGuide('key'); // clears value "key" for current guide

Canceling Events

An API for cancel is provided in the GuideRuntime that will prevent the next action, plus mark the event canceled for other listeners attached to the event. Available in 2.147.0 and later on the following event phases:

  • beforeAdvance
  • beforePrevious
  • beforeDismiss
  • beforeSnooze

To cancel during one of these phases, add a property "cancel" set to true on the event object passed into the runtime function.

// GuideRuntime is attached as 'this' in the Step's script
this.on('beforeAdvance', evt => {
if (!form.isValid()) {
evt.cancel = true;
}
});

The beforeMount event is available in 2.234.0 or later. Due to technical limitations it cannot be subscribed with this.on() from the script function. It needs to be specified as a key in a globalScripts entry:

pendo.initialize({
guides: {
globalScripts: [
{
beforeMount: function (evt) {
console.log('This runs before the guide displays');
if (shouldCancel) {
evt.cancel = true; // set cancel to true to prevent the guide from displaying
}
}
}
]
}
});