Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support page level full redirection #43

Merged
merged 6 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions documentation/experiments.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,17 @@ Start and end dates are in the flexible JS [Date Time String Format](https://tc3

So you can both use generic dates, like `2024-01-31` or `2024/01/31`, and time-specific dates like `2024-01-31T13:37` or `2024/01/31 1:37 pm`. You can even enforce a specific timezone so your experiment activates when, say, it's 2am GMT+1 by using `2024/1/31 2:00 pm GMT+1` or similar notations.

#### Full redirects
For the use case that fully redirect to the target URL instead of just replacing the content, you could add a new property `Experiment Resolution | redirect` in page metadata:
| Metadata | |
|-----------------------|--------------------------------------------------------------|
| Experiment | Hero Test |
| Experiment Variants | [https://{ref}--{repo}--{org}.hlx.page/my-page-variant-1](), [https://{ref}--{repo}--{org}.hlx.page/my-page-variant-2](), [https://{ref}--{repo}--{org}.hlx.page/my-page-variant-3]() |
| Experiment Resolution | redirect
FentPams marked this conversation as resolved.
Show resolved Hide resolved

Same for the audience and campaign personalization, by adding the following property to redirect to target URL:
`Audience Resolution: redirect`
`Campaign Resolution: redirect`
### Section-level experiments

Each section in a page can also run 1 experiment, so you can have as many section-level experiments as you have sections.
Expand Down
58 changes: 46 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,36 @@
: '';
}

/**
* Fires a Real User Monitoring (RUM) event based on the provided type and configuration.
* @param {string} type - the type of event to be fired ("experiment", "campaign", or "audience")
* @param {Object} config - contains details about the experience
* @param {Object} pluginOptions - default plugin options with custom options
* @param {string} result - the URL of the served experience.
*/
function fireRUM(type, config, pluginOptions, result) {
const { selectedCampaign = 'default', selectedAudience = 'default' } = config;

const typeHandlers = {
experiment: () => ({
source: config.id,
target: result ? config.selectedVariant : config.variantNames[0],
}),
campaign: () => ({
source: result ? toClassName(selectedCampaign) : 'default',
target: Object.keys(pluginOptions.audiences).join(':'),
}),
audience: () => ({
source: result ? toClassName(selectedAudience) : 'default',
target: Object.keys(pluginOptions.audiences).join(':'),
}),
};

const { source, target } = typeHandlers[type]();
const rumType = type === 'experiment' ? 'experiment' : 'audience';
window.hlx?.rum?.sampleRUM(rumType, { source, target });
}

/**
* Sanitizes a name for use as a js property name.
* @param {String} name The unsanitized name
Expand Down Expand Up @@ -321,6 +351,7 @@
* Creates an instance of a modification handler that will be responsible for applying the desired
* personalized experience.
*
* @param {String} type The type of modifications to apply
* @param {Object} overrides The config overrides
* @param {Function} metadataToConfig a function that will handle the parsing of the metadata
* @param {Function} getExperienceUrl a function that returns the URL to the experience
Expand All @@ -329,6 +360,7 @@
* @returns the modification handler
*/
function createModificationsHandler(
type,
overrides,
metadataToConfig,
getExperienceUrl,
Expand All @@ -344,6 +376,13 @@
const url = await getExperienceUrl(ns.config);
let res;
if (url && new URL(url, window.location.origin).pathname !== window.location.pathname) {
if (toClassName(metadata?.resolution) === 'redirect') {
// Firing RUM event early since redirection will stop the rest of the JS execution
fireRUM(type, config, pluginOptions, url);
ramboz marked this conversation as resolved.
Show resolved Hide resolved
window.location.replace(url);

Check warning on line 382 in src/index.js

View check run for this annotation

Codecov / codecov/patch

src/index.js#L381-L382

Added lines #L381 - L382 were not covered by tests
// eslint-disable-next-line consistent-return
return;
}

Check warning on line 385 in src/index.js

View check run for this annotation

Codecov / codecov/patch

src/index.js#L384-L385

Added lines #L384 - L385 were not covered by tests
// eslint-disable-next-line no-await-in-loop
res = await replaceInner(new URL(url, window.location.origin).pathname, el);
} else {
Expand Down Expand Up @@ -479,6 +518,7 @@
cb,
) {
const modificationsHandler = createModificationsHandler(
type,
getAllQueryParameters(paramNS),
metadataToConfig,
getExperienceUrl,
Expand Down Expand Up @@ -699,16 +739,14 @@
parseExperimentManifest,
getUrlFromExperimentConfig,
(el, config, result) => {
fireRUM('experiment', config, pluginOptions, result);
// dispatch event
const { id, selectedVariant, variantNames } = config;
const variant = result ? selectedVariant : variantNames[0];
el.dataset.experiment = id;
el.dataset.variant = variant;
el.classList.add(`experiment-${toClassName(id)}`);
el.classList.add(`variant-${toClassName(variant)}`);
window.hlx?.rum?.sampleRUM('experiment', {
source: id,
target: variant,
});
document.dispatchEvent(new CustomEvent('aem:experimentation', {
detail: {
element: el,
Expand Down Expand Up @@ -802,15 +840,13 @@
parseCampaignManifest,
getUrlFromCampaignConfig,
(el, config, result) => {
fireRUM('campaign', config, pluginOptions, result);
// dispatch event
const { selectedCampaign = 'default' } = config;
const campaign = result ? toClassName(selectedCampaign) : 'default';
el.dataset.audience = selectedCampaign;
el.dataset.audiences = Object.keys(pluginOptions.audiences).join(',');
el.classList.add(`campaign-${campaign}`);
window.hlx?.rum?.sampleRUM('audience', {
source: campaign,
target: Object.keys(pluginOptions.audiences).join(':'),
});
document.dispatchEvent(new CustomEvent('aem:experimentation', {
detail: {
element: el,
Expand Down Expand Up @@ -885,14 +921,12 @@
parseAudienceManifest,
getUrlFromAudienceConfig,
(el, config, result) => {
fireRUM('audience', config, pluginOptions, result);
// dispatch event
const { selectedAudience = 'default' } = config;
const audience = result ? toClassName(selectedAudience) : 'default';
el.dataset.audience = audience;
el.classList.add(`audience-${audience}`);
window.hlx?.rum?.sampleRUM('audience', {
source: audience,
target: Object.keys(pluginOptions.audiences).join(':'),
});
document.dispatchEvent(new CustomEvent('aem:experimentation', {
detail: {
element: el,
Expand Down
Loading