Showpad App
import ShowpadApp from '@bbc/front-end-kit/js/components/ShowpadApp';
Most of our Showpad Applications use Highway js as a router. Therefore this class extends from our HighwayApp class to benefit from our common setup for Highway routed applications. This class adds all the necessary preparation logic for Showpad applications so that there is no need to go through the initial setup with library loading, config loading and config injection and repeating all that every time the view changes. It provides us with a generic and consistent way to set this up so that it improves maintainability and project transportability between devs.
Getting started
Just like App and HighwayApp, it is meant to be extended and use that child class as the base of the project that is being developed
export default class MainApp extends ShowpadApp {
// constructor
constructor(args = {}) {
// call super
super(_defaultsDeep(args, {
showpad: { /* ShowpadApp configuration */ },
/* HighwayApp configuration */
/* Media query definitions */
}));
}
// methods...
}
Initialisations
Like HighwayApp adds initialisation keys, ShowpadApp does too. This way it is possible to run logic during every step of the config loading/injection/building process
showpad:lib-loaded: When Showpad's library is loadedshowpad:user-loaded: When the current user's info has been fetchedshowpad:config-loaded: When Showpad's config.json is loaded into memoryshowpad:config-injected: When config injection into the DOM has happened (also fires on every Highway page change)
initReadyView(args = {}) {
// block method when key is not...
if (![
'init:resize', // on resize
'showpad:config-injected' // after config injected => happens on highway page changes too
].includes(args.key)) return
// run inits
}
Highway settings
An extra Highway setting is added that is purely made for developement. It is the index property.
export default class MainApp extends ShowpadApp {
// constructor
constructor(args = {}) {
// call super
super(_defaultsDeep(args, {
highway: {
index: '/'
}
}));
}
// methods...
}
Automatic Development Redirection
In our structure of developing for Showpad, it may be required to alway pass through the index in order to make sure everything initialised properly. Something that may not happen when development is happening on a sub page. This property will force the application to go redirect using a page load to the index and then redirect back to the originally requested page using the Highway router.
This way sub-pages don't need to have all the globally defined components in HTML and still show them as if they were thanks to the fact the redirection through Index happens.
Tips
This option can be disabled using preventDevRedirect in the showpad constructor object.
// class
export default class MainApp extends ShowpadApp {
// constructor
constructor(args = {}) {
// call super
super(_defaultsDeep(args, {
showpad: {
preventDevRedirect: false,
},
}));
}
//...
}
Automatic AppsDb initialisation
The instance provides the ability to automatically initialise AppsDb stores using our AppsDb class by default. This can be overridden if the custom class is provided. Mind that it will always look for the init() method.
export default class MainApp extends ShowpadApp {
// constructor
constructor(args = {}) {
// call super
super(_defaultsDeep(args, {
showpad: {
appsDbKeys: [
{ id: 'appsdb-local-store-id', type: 'local' },
{ id: 'appsdb-global-store-id', type: 'global' },
{ id: 'appsdb-custom-local-store-id', type: 'local', Class: CustomAppsDbClass },
{ id: 'appsdb-custom-global-store-id', type: 'global', Class: CustomAppsDbClass },
]
},
}));
}
// methods...
}
Build-in placeholder methods
Showpad App comes with some built-in Config methods that are automatically added bo the constructor args. These are complex, yet very handy when it comes to implementing ShowpadConfig configuration paths.
Placeholders can be defined for both javascript value retrievals and DOM value injections.
path
This provides the ability add regions to your HTML and let the config use them to generate the paths to the config values, shortening the define paths and greatly reducing the changes for mistakes at a slight cost of added complexitiy. It introduces a new data attribute: data-sp-region to define those regions in html.
DOM Injections only!
<div data-sp-region="region-1"></div>
<div data-sp-region="region-2"></div>
<div data-sp="{{ path }}.title"></div>
</div>
</div>
Will result in injecting the value in the html that was found on [labels/content].region-1.region-2.title.value
generalPath
Produces a path prefixed with general. followed by the data-sp-region chain of the element, e.g. general.region-1.region-2. Useful for config values that live under a shared general namespace rather than a view-specific path.
DOM Injections only!
<div data-sp-region="footer">
<span data-sp="{{ generalPath }}.cta-label"></span>
</div>
Will look up: [labels/content].general.footer.cta-label.value
i and i-*
This provides the ability to keep track of the index of the item it is applied to. DOM Injections only!
i-*: Same asìalthough it uses the * as a number or a key to alter the classic way of counting.i-[level]: Define the amount of levels it needs to go up in the dom and start counting from there. Handy for elements that are nested inside a wrapper but need to use the index of their parent wrapperi-[region]: Region key to use as counter id. Usesdata-sp-regionkeys as counter ids just like CSS does.data-sp-region'resets' the counter of that region, all items using that key will increase the counter by 1 everytime the key is used as placeholder in the config path.i: If no key or level is given, it will use the current region as the active counter.
Level example:
<ul>
<li>
<span data-sp="list.item-{{ i-1 }}"></span>
</li>
<li>
<span data-sp="list.item-{{ i-1 }}"></span>
</li>
<li>
<span data-sp="list.item-{{ i-1 }}"></span>
</li>
</ul>
Will result in injecting the value in the html that was found on:
[labels/content].list.item-1.value[labels/content].list.item-2.value[labels/content].list.item-3.value
Region example:
<ul data-sp-region="list">
<li>
<span data-sp="list.item-{{ i-list }}"></span>
</li>
<li>
<span data-sp="list.item-{{ i-list }}"></span>
</li>
<li>
<span data-sp="list.item-{{ i-list }}"></span>
</li>
</ul>
Will result in injecting the value in the html that was found on:
[labels/content].list.item-1.value[labels/content].list.item-2.value[labels/content].list.item-3.value
It goes up a parent and starts to count the index from that parent.
Combination example
An example of combining both built-in placeholders
<div class="content" data-sp-region="page">
<ul class="list-1" data-sp-region="list-1">
<li class="list-1__item"> <span data-sp="{{ path }}.item-{{ i }}"></span></li>
<li class="list-1__item">
<span data-sp="{{ path }}.item-{{ i }}"></span>
<ul class="list-2" data-sp-region="list-2">
<li class="list-2__item"><span data-sp="{{ path }}.item-{{ i }}"></span></li>
<li class="list-2__item"><span data-sp="{{ path }}.item-{{ i }}"></span></li>
</ul>
</li>
<li class="list-1__item"> <span data-sp="{{ path }}.item-{{ i }}"></span></li>
<li class="list-1__item">
<span data-sp="{{ path }}.item-{{ i }}"></span>
<ul class="list-2" data-sp-region="list-2">
<li class="list-2__item"><span data-sp="{{ path }}.item-{{ i-list-1 }}-{{ i }}"></span></li>
<li class="list-2__item"><span data-sp="{{ path }}.item-{{ i-list-1 }}-{{ i }}"></span></li>
</ul>
</li>
</ul>
</div>
Will result in injecting the value in the html that was found on:
[labels/content].page.list-1.item-1.value[labels/content].page.list-1.item-2.value[labels/content].page.list-1.list-2.item-1.value[labels/content].page.list-1.list-2.item-2.value[labels/content].page.list-1.item-3.value[labels/content].page.list-1.item-4.value[labels/content].page.list-1.list-2.item-4-1.value[labels/content].page.list-1.list-2.item-4-2.value
API
constructor(args = {})
The constructor method initialises the new ShowpadApp instance.
Parameters
args: The configuration object used by the constructor to initialise the ShowpadApp in the desired way.showpad: Contains the settings for the ShowpadApp instanceappsDbKeys: Array of AppsDb definition objects. Each entry is passed directly tonew (def.Class || AppsDB)(def), so anyAppsDbconstructor argument (id,type,accessToken,oauthInstance, …) can be set per entry.init()is called on each only when the current user is an admin.configGen: Activates the config builder on every page- and view-load.placeholders: Placeholder value & method logic that will be passed to theShowpadConfiginstance.preventDevRedirect: Disables the rediraction through index (on page refresh) during development
highway: Contains the settings for theHighwayAppinstance (parent of ShowpadApp)index: Sets the index to redirect from when developing on a sub page, this way we don't need to navigate back to the page that is being developed. (addition by ShowpadApp!)devRedirectTimeout(number, default500): Milliseconds to wait before Highway redirects back to the originally-requested page after the dev redirect through index.
mediaQueries: The mediaquery definition collection it needs to initialise for
Methods
navigateIn(args = {})
If present, it will be called when Highway's Navigate_IN' is fired. Args object contains the information from the Highway event.
Tips
Make sure to call super.navigateIn() when extended.
navigateOut(args = {})
If present, it will be called when Highway's Navigate_OUT is fired. Args object contains the information from the Highway event.
Tips
Make sure to call super.navigateOut() when extended.
navigateEnd(args = {})
If present, it will be called when Highway's Navigate_END is fired. Args object contains the information from the Highway event.
Tips
Make sure to call super.navigateEnd() when extended.
Getters & Setters
Showpad
The Showpad library instance the ShowpadApp is using
showpadConfig
The ShowpadConfig instance the app is using.
configGen
Boolean signaling if the config is generated on every pageload and view load.
configLoaded
Boolean signaling that the config is loaded in the ShowpadConfig instance.
Public properties
user
The current Showpad user object, populated automatically after Showpad.getUserInfo() resolves. Contains at minimum id, full_name, and isAdmin (boolean — set to true only when online and the user is confirmed as admin).
Private properties
_appsDbs
Instances of the AppsDbs it has initialised during loading
_configGen
Boolean flag to activate Config object generation on page load.
_showpadConfig
The Config instance it uses for Showpad config loading & dom injection.
External dependencies
This class uses some external dependencies that may be necessary to install using NPM.
- Experience APP SDK: Showpad's library
- lodash: For some quick and usefull utility methods like:
capitalize: To capitalize strings so thatinit[name]can be written in camelCasedefaultsDeep: Used to provide objects with default values.camelCase: Turns strings into camel case casing.kebabCase: Turns strings into kebab case casing.
Todo
- Use named methods for specific but reocurring scenarios instead of having to use checks on each method.
- Separate Built-in placeholder methods to utils file so they are ready to be reused in other scenarios