Including Elements
HAPI Elements are a set of widgets that you can include with only minimal lines of code. Each widget servers a different purpose. The type of widgets are:
- Widgets - Bits and pieces of the application, for you to construct your own custom user journeys
- Widget Sections - Not all but some of the Widgets also have sections exposed
- User Journey Widgets - These widgets provide a user journey flow by including the Widgets and Widget sections. Two examples are Product and Contract Ordering User Journeys
The hierarchy of the user journey widgets are as follows. Each box (subgraph) represents a User Journey widget and their connections. User Journey widgets in the outer layers already include the nested user journey widgets:
Loading...
Most of your business requirements can be handled by User Journey widgets without you needing to construct your own user journeys. With HAPI Elements Javascript API, you can hide the step(s) listed above according to your business requirements.
The written hierarchy is as follows:
- Order Journey Product -> he-orderjourney-product-shorter
- Product Journey -> he-product-journey
- Order Journey Product Checkout -> he-orderjourney-product-checkout-shorter
- Order Journey Contract -> he-orderjourney-contract-shorter
- Contract Journey -> he-contract-journey
- Order Journey Contract Checkout -> he-orderjourney-contract-checkout-shorter
The widgets are broken down into smaller chunks so that you can mix and match these the widgets mentioned in the diagrams in different pages/sections in your application according to your business logic. There are some use cases below to help you better understand different use cases for each of the widget:
As an ATS Product Manager/Developer;
I want to show Product Search & Select Journey in X page and Product Checkout in Y page
- Use
he-product-journey
on X page - Use
he-orderjourney-product-checkout-shorter
in Y page
- Use
I have no use cases for Contracts and only want to display Product Order Journey
- Use
he-orderjourney-product-shorter
- Use
I have no use cases for Products and only want to display Contract Order Journey
- Use
he-orderjourney-contract-shorter
- Use
I want to show Product Search & Select Journey on X page, Contract Journey on Y page then complete the order of the selected products/contracts in a common Z page
- Use
he-product-journey
on X page - Use
he-contract-journey
on Y page - Use
he-orderjourney-checkout-shorter
on Z common page
- Use
I have some internal marketing strategy: I want to allow users to create their own contracts and let them add it to the basket but have a CTA as a paywall when they actually try to order contracts
- Use
he-contract-journey
, let them add their own contract and let them add it to their campaign - Have a CTA button somewhere that is supposed to redirect user or open a modal with the paywall, once they upgrade, start showing
he-orderjourney-contract-shorter
- Use
I have some internal marketing strategy: I want to allow users to create their own contracts but when they click on "Add to Campaign" button, open a popup etc. with a paywall and actually prevent them from adding to the basket unless they upgrade
- Use either one of these flows according to your business logic:
he-orderjourney-contract-shorter
he-contract-journey
- This can be achieved by throwing an error inside the callback function that you can provide to the
onStart
listener of service functions. More info about service callback functions are documented on Service Submodule page
- Use either one of these flows according to your business logic:
Order Journey Product Widget Steps
Loading...
Product Journey Steps
Loading...
Order Journey Product Checkout Steps
Loading...
Order Journey Contract Widget Steps
Loading...
Contract Journey Steps
Loading...
Order Journey Contract Checkout Steps
Loading...
Some steps are hidden from the end user
There are two more steps in the Order Journey that are hidden. These are:
- Posting URLs (contains form fields for Job Page URL and Application URL)
- Posting UTM Codes (contains UTM Tracking Codes for each product & contract in the campaign about to be ordered)
Because these are technical and end user does not know how to set these up, you need to prefill these form fields. To do that, please refer to Prefilling Campaign Order Form.
You may need to see these steps during your development to see if prefilling indeed works. You can enable these steps with the following code:
window
.hapi
.orderJourney
.state
.stepsEnabled = [
...window.hapi.orderJourney.state.stepsEnabled.value,
window.hapi.orderJourney.utils.stepKeys.postingURLs,
window.hapi.orderJourney.utils.stepKeys.postingUTMCodes,
]
Please note that the order of stepsEnabled
is important thus the hidden steps of Posting URLs and Posting UTM Codes will be added to the end of the order journey which means it will be after the Order Confirmation step. If you must show these steps to your end user make sure to enable the steps according to this order:
window.hapi.orderJourney.utils.stepKeys.postingURLs
should come afterwindow.hapi.orderJourney.utils.stepKeys.postingWorkingLocation
window.hapi.orderJourney.utils.stepKeys.postingUTMCodes
should come beforewindow.hapi.orderJourney.utils.stepKeys.orderReview
To go to these steps you can either use:
- the "Active Step" select form field inside "Order Journey" in the
ELEMENTS DEBUG
panel that we automatically add to your application when not in production environment - or the following code snippet:
const switchToStepByIndex = (index) => {
window
.hapi
.orderJourney
.state
.stepActiveIndex = index
}
const getStepIndex = (stepKey) => {
return window.hapi.orderJourney.state.steps.value.findIndex(step => step.key === stepKey)
}
let stepIndex
// Jump to window.hapi.orderJourney.utils.stepKeys.postingURLs
stepIndex = getStepIndex(window.hapi.orderJourney.utils.stepKeys.postingURLs)
switchToStepByIndex(stepIndex)
// Jump to window.hapi.orderJourney.utils.stepKeys.postingUTMCodes
stepIndex = getStepIndex(window.hapi.orderJourney.utils.stepKeys.postingUTMCodes)
switchToStepByIndex(stepIndex)
Widget naming conventions
Widgets comply to the following naming pattern: he-MODULE-WIDGET-SECTION
Module name refers to the following values:
- router
- config
- ats
- basket
- campaign
- contract
- product
- common
- wallet
- debugging
- language
- theming
- orderJourney
- userJourney
- alert
- modal
- ui
- experimental
You can check which widgets and widget sections are available on the Playground.
Several examples:
he-orderjourney-product-shorter
contains only searching/selecting and ordering products without contract stepshe-orderjourney-contract-shorter
contains only adding/selecting and ordering contracts without product stepshe-contract-add
contains only the form that adds a contract without any listing behaviourshe-contract-add-credentials
contains credentials section of Contract Add form
The widgets apart from the user journey widgets, are only available for extreme use cases and for the most part, you can use the user journey widgets instead of trying to construct your own user journeys by including tens of different widget and widget sections.
To include a widget in your application, including the web component in your HTML:
<he-orderjourney-product-shorter></he-orderjourney-product-shorter>
Under the hood, the web component injects an iframe with minimal height and width, and then a package we use called iFrameResizer
takes place and resizes the iframe to fit the natural width and height. By default, iframes have minimum 300px width and 150px height; resizing with this package allows widgets to perform best in your UI. You can disable the resize behaviour by adding resize="false"
attribute on the web component.
Until the iframe loads, a loading spinner will be shown in place, so iframe is with display: none
then display: block
.
The web component that injects the iframe is opinionated to provide the most elasticity. It has, by default, the following CSS applied:
You can provide your own values via "style.width", "style.height" etc.
maxHeight = style.maxHeight || "100%"
display = style.display || "block"
height = style.height || "100%"
overflow = style.overflow || "hidden"
Detecting when iframes have loaded and/or unloaded
To detect when an iframe loads and/or unloads, refer to UI Submodule > Detecting when iframes have loaded and/or unloaded section.
If you have followed the steps in Setting Up and you can see the widget, that means you have completed the integration!
Notes about UI widgets like Alert Bar and Modal
When HAPI Elements loads, by default, we inject into your application body
two elements:
- he-ui-modals
- he-ui-alertbar
If you don't want this default behaviour and, for example, you want to show your own alerts instead of HAPI Elements alerts; then you can add adjust your settings on window.hapiInjector
before HAPI Elements loads:
window.hapiInjector.setConfig("addModal", false)
window.hapiInjector.setConfig("addAlertBar", false)
Please refer to How to use your own modals and How to use your own alerts.
Lazy Loading Iframes
In some cases the performance may get reduced when loading many HAPI Elements widgets are present on the page. We recommend adding lazy-load
attribute to the web component so that the iframe loads only when in viewport to increase load times and performance.
It works by adding loading="lazy"
attribute to the iframe. Please read more about loading attribute on MDN
Here is an example:
<he-userjourney-buttons
lazy-load="false"
></he-userjourney-buttons>
When loading web components lazily, unless they have already been rendered on the screen (iframe loaded), the service functions etc. on HAPI Elements JS API may not work properly as some functionalities require a UI to be rendered on the screen.
Using HAPI Elements inside Iframes
A regular integration with HAPI Elements looks like:
Loading...You may want to encapsulate HAPI Elements inside an iframe of your own as such:
Loading...When HE is inside an iframe, the functionality of HE such as the Oauth redirects, because Oauth redirect will be made to the iframe, will remain to work within your iframe.
HAPI Elements, before Oauth, grabs existing query parameters from the URL and restores them after the redirect. Because this URL is inside the iframe, you might want to add logic for communicating in your site with your own iframe so that any query parameters that you want the iframe to restore, can be sent from the main site. Here is an example:
// your main site
<iframe src="https://my-ats.com/my-own-iframe-for-elements?myOwnQueryParameters=myOwnValue"></iframe>
After the redirect the query parameters are going to be restored ?myOwnQueryParameters=myOwnValue
. If you want to control the behaviors in and out of the iframe, then you need to use window.postMessage API to exchange messages to/from your iframe; this way you can control the query parameters and HE from outside the iframe (your main site).
Some technical notes about widgets
HAPI Elements does not have a routing system
HAPI Elements widgets don't have any routing capabilities, meaning that the widgets are only responsible for presenting the data they receive and managing it. In other words, we don't have any buttons/links in any widget linking to other widgets. That part needs to be handled by the ATS by creating custom pages on your application, then embedding the appropriate widgets and tying in your routing system with your own buttons/links. There is an example on the Demo ATS Contract Journey on Separate Pages demonstrating this behaviour. Please note that we can still have virtualized tabs, meaning that some user journeys that make sense can be combined into one that works with a tab behaviour behind the scenes without actually changing the app route; this can also be seen on Demo ATS Contract Journey Page
An example is the Contract List and Contract Add widgets. We don't have a "Create a Contract" button in the Contract List widget, nor do we have a "Back to the Contract List" button in the Contract Add widget. You need to create pages for these two with your own buttons and routing system however you can still present your business use case to your Partner Account Manager, and we can consider adding widgets that contain a virtualized routing (kind of virtual tabs) that can handle creating and listing functionality together.
As HAPI Elements widgets don't have any routing system, upon a successful addition of a contract, the user is not sent to the "list" widget. After the addition, the widget is reset back to its default state.
In version 1.0, a new user journey widget has been added that handles Contract Listing and Adding together called he-contract-journey
. The example above uses the Contract List and Contract Add widgets, but that example does not apply anymore because of the addition of this new widget; however, it may apply in other cases where a widget is needed and does not exist. In that case, please consult your Partner Account Manager to make a request.
Contracts that have Oauth
Depending on the channel you want to add as a contract, the Contracts Add widget may have OAuth authentication method, which redirects the user to an authentication page of the channel. Upon successful authentication, the user is sent back to the URL they came from via a callback. For this reason, it is really important that you create a page with a route that can be restored. It can be a page like /vonq/contract/add
or a query parameter route like ?openModal=vonq-contracts
, as long as when a user lands back on it, they can see the HAPI Elements widget. If you show the widget in a modal or a tab, the modal or tab that contains the contract widget should open when page loads. So for example if you have ?openModal=vonq-contracts
, if you refresh your application page, then modal should open and contracts widget should be shown.
When the user clicks on the "Authenticate" button, we automatically grab your existing URL with query parameters (if any) and then append the necessary query parameters for the automatic restore behaviour of the OAuth hash. You don't need to handle the "OAuth callback" as long as the user can see the widget when they are back to your application as we are handling it automatically. User will be presented with the channel they had selected before the redirect, preselected and the hash populated behind the scenes as part of the Contract's credentials. The button will turn to "You are now authenticated".
If you want to manually restore the query parameters, you can do as such:
We may have similar logic in the future in other areas for query params thus we recommend that on every app load (your app, not elements), you do a check:
if (window.location.search) {
// this is a string
// store it somewhere in your app
// like state or localStorage or cookies
// (optional) then reset window.location.search so that
// you don't need to retain it in your URL
}
// later when HAPI Elements loads,
window.addEventListener("hapi:load:script", () => {
const cachedLocationSearch = cookies.get("windowLocationSearch")
// if you already have an object containing query params key->values, you can skip this line
const queryParams = window.hapiUtils.router.convertSearchStringToQueryParams(cachedLocationSearch) //returns Record<string, string | string[]>
window.hapiRouting.setQueryParams(queryParams)
})
For ATSs that have hash routes
If you have a virtualized single page with a hash parameter such as something.com/somepage.jsp#/integrations/vonq/contracts
, automatic "oauth hash restore" functionality of HAPI Elements will work however when the end user lands back on the page, the contract widget won't be visible because user will be sent to the wrong page (page without the hash parameter). In that case you either need to change your implementation so that the contract widget is on an actual page like something.com/vonq-contracts.jsp
(or a query parameter when in a modal or a tab) or you will need to manually open the modal/tab.
Oauth spec states that the URL cannot have hash parameters:
The endpoint URI MUST NOT include a fragment component.
Therefore when user lands back on the wrong page without the hash parameter (for example if the page was something.com/somepage.jsp#/integrations/vonq/contracts
then user will land on somepage.jsp
without the hash part), you need to manually open the modal/tab then HAPI Elements will automatically make the channel user just authenticated with "preselected" and the hash
parameter will be grabbed from the URL and added to contract's credentials. To understand when to open the modal/tab manually you need some Javascript code like below in somepage.jsp
:
const queryParams = new URLSearchParams(window.location.search);
if (queryParams.has("defaultState")) {
// open modal/tab with contract widget
// HAPI Elements will automatically handle copying from query parameters to the form end user sees
}
If your application has some code that grabs the "hash" query parameter from the URL upon the app mount, you need to adjust your code so that it skips handling the "hash" query parameter for pages that embed HAPI Elements widgets.