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 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.
Guide Lifecycle
Vue lifecycle events were consulted to begin this document.
JavaScript is running after it's included inside the Guide so it can't see the events preceding its existence like beforeCreate, created, and beforeMount.
List of events:
beforeUpdate
position of guide is about to moveupdated
position of guide has movedbeforeUnmount
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)beforeAdvance
called before guide has been Advancedadvanced
guide was unmounted due to being AdvancedbeforeSnooze
called before guide has been Snoozedsnoozed
guide was unmounted due to be snoozedbeforeDismiss
called before guide has been Dismisseddismissed
guide was unmounted due to being DismissedbeforePrevious
called before guide has been "Advanced" to Previous Stepprevious
guide was unmounted due to be "Advanced" to Previous Stephidden
guide was unmounted due to being Hidden
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', () => {});
// Future work
this.advanceOn({element: button, type: 'click'});
this.hideOn({type: 'keypress', keycode: 'Escape'});
// CodeBlock Function<GuideRuntime> - GuideRuntime is available in the this variable inside the function
this.advanceOn({
type: 'mouseover',
target: 'h1.title'
})
this.on('advanced', () => {
alert('You are done!');
});
this.on('dismissed', () => {});
this.on('mounted', () => {});
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:
pendo.initialize({
visitor: { id: 12345 },
account: { id: 789 },
guides: {
globalScripts: [
{
script: function(step, guide) {
console.log('Step ', step.id, ' has run');
},
test: function(step, guide) { // Only run this on a specific known step id
return step.id === 'lCQ_9DAgLJlfA6nibXYmUf-HucE';
}
},
{
script: function(step, guide) {
console.log('Step is a second step')
},
test: function(step, guide) { // Run on the second step of any guide
return pendo._.findIndex(guide.steps, (s) => s.id === step.id) === 1;
}
},
{
script: function(step, guide) {
console.log('This guide has a badge');
},
test: function(step, guide) { // Run on all steps for guides that have a badge launch method
return guide.launchMethod.includes('badge');
}
},
{
script: function(step, guide) {
console.log('This step matches the naming criteria')
},
regex: /- Delay Script/ // Run on all steps where the guide name or step name has "- Delay Script" in it
}
]
}
});
globalScripts
- an array of objects that each define a custom global scriptscript
- (required) custom function goes heretest
- (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 guideregex
- (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
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 accessible 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.
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"
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;
}
});