Gate a tour step on what the screen actually renders
A tour step waits for its data-tour target and then gives up. If the element is one the screen only sometimes draws, give the step a condition, and a step that cannot work is dropped before the tour starts.
The failure a condition prevents is silent. The tour opens the step’s screen and waits up to two seconds for the target. When it never appears nothing errors and nothing is logged: the tip is shown floating over the page, attached to nothing. You only find out by running the tour on a site where the element is missing.
- When it runs. Once, when the tour launches, before the step’s
screenis worked out.
falseremoves the step entirely: the step counter is one shorter and nothing is greyed out. A tour whose every step is removed does not start. - It receives the show and may be async. The signature is
condition?: ( showId: number | null ) => boolean | Promise<boolean>. A plain() => booleanis valid and is the usual case. Return a promise when only the server knows the answer, as in the second step of the example. - Answer
falseon every failure — a timeout, an error response, an unexpected shape,
no show selected. A dropped step is invisible; a kept step whose target never renders is the pause and the tip attached to nothing. - Ask; do not read a cached copy of the answer. A tour can be launched from the Tours
catalog on any screen and from the setup wizard, so the data you expect to be loaded often is not, and a cold cache reads as “no”. - Share the predicate with whatever renders the element. If your component decides
whether to draw the target with a function, call the same function here. Two copies of “is it on screen?” drift apart. - A condition is not a dynamic screen.
screen: 'episode-references'asks whether there
is anywhere to go; a condition asks whether the step is wanted. A step can need both. - The tour-level
conditionis different and must stay synchronous. It decides whether
the whole tour is offered and is called without awaiting, so an async one returns a promise, which is truthy, and the tour is always offered.
Three values on window.benecasterAdmin answer the questions conditions usually ask: addons (what the license grants), installedAddons (what is installed), and nativeBridgeActive, which is true when a show has chosen Benecaster’s built-in membership and has at least one active tier. It does not mean Stripe is connected, so it is the right gate for a step about the Memberships screens, aimed at someone still setting up. All three are read when the admin page loads.
Code
window.BenecasterExtensions = window.BenecasterExtensions || {};
window.BenecasterExtensions.tours = window.BenecasterExtensions.tours || [];
window.BenecasterExtensions.tours.push( {
id: 'my-addon',
label: 'My Add-on',
steps: [
{
id: 'my-addon-intro',
target: '[data-tour="addon-card-benecaster-addon-my-addon"]',
screen: 'addons',
title: 'Where your add-on lives',
content: 'Switch it on per show here.',
},
{
id: 'my-addon-usage',
target: '[data-tour="my-addon-usage"]',
screen: 'addons',
title: 'Your usage this month',
content: 'Only shown once there is something to chart.',
// Only the server knows whether this show has usage to chart.
condition: async ( showId ) => {
if ( null === showId || showId <= 0 ) {
return false; // No show selected: nothing to ask about.
}
try {
const res = await fetch(
`${ window.benecasterAdmin.apiUrl }shows/${ showId }/my-addon/usage`,
{ headers: { 'X-WP-Nonce': window.benecasterAdmin.nonce } }
);
if ( ! res.ok ) {
return false;
}
const data = await res.json();
return Array.isArray( data.rows ) && data.rows.length > 0;
} catch {
return false; // When in doubt, drop the step.
}
},
},
],
} );
Need this built rather than just documented? See our services →