diff --git a/libs/blocks/ost/ost.js b/libs/blocks/ost/ost.js index 9121fdc29f6..69d798750db 100644 --- a/libs/blocks/ost/ost.js +++ b/libs/blocks/ost/ost.js @@ -1,5 +1,8 @@ import ctaTextOption from './ctaTextOption.js'; -import { getConfig, getLocale, getMetadata, loadScript, loadStyle } from '../../utils/utils.js'; +import { + getConfig, getLocale, getMetadata, loadScript, loadStyle, createTag, +} from '../../utils/utils.js'; +import { initService, loadMasComponent, getMasLibs, getMiloLocaleSettings, MAS_COMMERCE_SERVICE } from '../merch/merch.js'; export const AOS_API_KEY = 'wcms-commerce-ims-user-prod'; export const CHECKOUT_CLIENT_ID = 'creative'; @@ -8,12 +11,24 @@ const IMS_COMMERCE_CLIENT_ID = 'aos_milo_commerce'; const IMS_SCOPE = 'AdobeID,openid'; const IMS_ENV = 'prod'; const IMS_PROD_URL = 'https://auth.services.adobe.com/imslib/imslib.min.js'; -const OST_SCRIPT_URL = 'https://mas.adobe.com/studio/ost/index.js'; -const OST_STYLE_URL = 'https://mas.adobe.com/studio/ost/index.css'; +const OST_SCRIPT_URL = '/studio/ost/index.js'; +const OST_STYLE_URL = '/studio/ost/index.css'; /** @see https://git.corp.adobe.com/PandoraUI/core/blob/master/packages/react-env-provider/src/component.tsx#L49 */ export const WCS_ENV = 'PROD'; export const WCS_API_KEY = 'wcms-commerce-ims-ro-user-cc'; export const WCS_LANDSCAPE = 'PUBLISHED'; +export const WCS_LANDSCAPE_DRAFT = 'DRAFT'; +export const LANDSCAPE_URL_PARAM = 'commerce.landscape'; +export const DEFAULTS_URL_PARAM = 'commerce.defaults'; + +function isMasDefaultsEnabled() { + const searchParameters = new URLSearchParams(window.location.search); + const defaults = searchParameters.get(DEFAULTS_URL_PARAM) || 'on'; + return defaults === 'on'; +} + +let masCommerceService; +const masDefaultsEnabled = isMasDefaultsEnabled(); /** * Maps Franklin page metadata to OST properties. @@ -31,26 +46,45 @@ const priceDefaultOptions = { exclusive: false, }; -const updateParams = (params, key, value) => { - if (value !== priceDefaultOptions[key]) { +const updateParams = (params, key, value, opts) => { + if (value !== opts[key]) { params.set(key, value); } }; document.body.classList.add('tool', 'tool-ost'); +/** + * Gets the base URL for loading Tacocat OST build file based on maslibs parameter + * @returns {string} Base URL for OST index.js + */ +export function getMasLibsBase() { + const urlParams = new URLSearchParams(window.location.search); + const masLibs = urlParams.get('maslibs'); + + if (!masLibs || masLibs.trim() === '' || masLibs.trim() === 'main') return 'https://mas.adobe.com'; + + return getMasLibs().replace('/web-components/dist', ''); +} + /** * @param {Commerce.Defaults} defaults */ -export const createLinkMarkup = ( +export const createLinkMarkup = async ( defaults, offerSelectorId, type, offer, options, promo, + country, ) => { const isCta = !!type?.startsWith('checkout'); + const cs = offer.customer_segment; + const ms = offer.market_segments[0]; + const taxFlags = masDefaultsEnabled && masCommerceService + ? await masCommerceService.resolvePriceTaxFlags(country, null, cs, ms) + : {}; const createHref = () => { const params = new URLSearchParams([ @@ -77,11 +111,17 @@ export const createLinkMarkup = ( displayOldPrice, forceTaxExclusive, } = options; - updateParams(params, 'term', displayRecurrence); - updateParams(params, 'seat', displayPerUnit); - updateParams(params, 'tax', displayTax); - updateParams(params, 'old', displayOldPrice); - updateParams(params, 'exclusive', forceTaxExclusive); + const optsMasDefaults = masDefaultsEnabled ? { + ...priceDefaultOptions, + seat: offer.customer_segment !== 'INDIVIDUAL', + tax: taxFlags.displayTax || priceDefaultOptions.tax, + exclusive: taxFlags.forceTaxExclusive || priceDefaultOptions.exclusive, + } : priceDefaultOptions; + updateParams(params, 'term', displayRecurrence, optsMasDefaults); + updateParams(params, 'seat', displayPerUnit, optsMasDefaults); + updateParams(params, 'tax', displayTax, optsMasDefaults); + updateParams(params, 'old', displayOldPrice, optsMasDefaults); + updateParams(params, 'exclusive', forceTaxExclusive, optsMasDefaults); } return `https://milo.adobe.com/tools/ost?${params.toString()}`; }; @@ -102,36 +142,38 @@ export async function loadOstEnv() { const commerceEnv = searchParameters.get('commerce.env'); if (wcsLandscape || commerceEnv) { if (wcsLandscape) { - searchParameters.set('commerce.landscape', wcsLandscape); + searchParameters.set(LANDSCAPE_URL_PARAM, wcsLandscape); searchParameters.delete('wcsLandscape'); } if (commerceEnv?.toLowerCase() === 'stage') { - searchParameters.set('commerce.landscape', 'DRAFT'); + searchParameters.set(LANDSCAPE_URL_PARAM, WCS_LANDSCAPE_DRAFT); searchParameters.delete('commerce.env'); } window.history.replaceState({}, null, `${window.location.origin}${window.location.pathname}?${searchParameters.toString()}`); } /* c8 ignore next */ - const { initService, loadMasComponent, getMasLibs, getMiloLocaleSettings, MAS_COMMERCE_SERVICE } = await import('../merch/merch.js'); - await initService(true, { 'allow-override': 'true' }); + const attributes = { 'allow-override': 'true' }; + if (masDefaultsEnabled) { + attributes['data-mas-ff-defaults'] = 'on'; + } + await initService(true, attributes); // Load commerce.js based on masLibs parameter - await loadMasComponent(MAS_COMMERCE_SERVICE); + masCommerceService = await loadMasComponent(MAS_COMMERCE_SERVICE); // Get the exports - they might be in different places depending on how it was loaded let Log; let Defaults; - let resolvePriceTaxFlags; if (getMasLibs() && window.mas?.commerce) { // Loaded from external URL - check global scope - ({ Log, Defaults, resolvePriceTaxFlags } = window.mas.commerce); + ({ Log, Defaults } = window.mas.commerce); } else { // Loaded as module - ({ Log, Defaults, resolvePriceTaxFlags } = await import('../../deps/mas/commerce.js')); + ({ Log, Defaults } = await import('../../deps/mas/commerce.js')); } const defaultPlaceholderOptions = Object.fromEntries([ ['term', 'displayRecurrence', 'true'], - ['seat', 'displayPerUnit', 'true'], + ['seat', 'displayPerUnit', masDefaultsEnabled ? null : 'true'], ['tax', 'displayTax'], ['old', 'displayOldPrice'], ].map(([key, targetKey, defaultValue = false]) => { @@ -178,7 +220,7 @@ export async function loadOstEnv() { window.history.replaceState({}, null, newURL); const environment = searchParameters.get('env') ?? WCS_ENV; - const landscape = searchParameters.get('commerce.landscape') ?? WCS_LANDSCAPE; + const landscape = searchParameters.get(LANDSCAPE_URL_PARAM) ?? WCS_LANDSCAPE; const owner = searchParameters.get('owner'); const referrer = searchParameters.get('referrer'); const repo = searchParameters.get('repo'); @@ -220,21 +262,23 @@ export async function loadOstEnv() { } } - const onSelect = ( + const onSelect = async ( offerSelectorId, type, offer, options, promoOverride, + co, ) => { log.debug(offerSelectorId, type, offer, options, promoOverride); - const link = createLinkMarkup( + const link = await createLinkMarkup( Defaults, offerSelectorId, type, offer, options, promoOverride, + co, ); log.debug(`Use Link: ${link.outerHTML}`); @@ -260,20 +304,53 @@ export async function loadOstEnv() { defaultPlaceholderOptions, wcsApiKey: WCS_API_KEY, ctaTextOption, - resolvePriceTaxFlags, + resolvePriceTaxFlags: masCommerceService?.resolvePriceTaxFlags, modalsAndEntitlements: true, }; } +function addToggleSwitch(container, label, checked, onChange) { + const switchDiv = createTag('div', { class: 'spectrum-Switch' }, null, { parent: container }); + const input = createTag('input', { type: 'checkbox', class: 'spectrum-Switch-input', id: 'gb-overlay-toggle' }, null, { parent: switchDiv }); + createTag('span', { class: 'spectrum-Switch-switch' }, null, { parent: switchDiv }); + createTag('label', { class: 'spectrum-Switch-label', for: 'gb-overlay-toggle' }, label, { parent: switchDiv }); + input.checked = checked; + input.addEventListener('change', onChange); +} + +function addToggleSwitches(el, ostEnv) { + const { base } = getConfig(); + loadStyle(`${base}/blocks/graybox/switch.css`); + const toggleContainer = createTag('span', { class: 'toggle-switch' }, null, { parent: el }); + addToggleSwitch(toggleContainer, 'Draft landscape offer', ostEnv.landscape === WCS_LANDSCAPE_DRAFT, (e) => { + const url = new URL(window.location.href); + if (e.target.checked) { + url.searchParams.set(LANDSCAPE_URL_PARAM, WCS_LANDSCAPE_DRAFT); + } else { + url.searchParams.delete(LANDSCAPE_URL_PARAM); + } + window.location.href = url.toString(); + }); + addToggleSwitch(toggleContainer, 'MAS defaults', masDefaultsEnabled, (e) => { + const url = new URL(window.location.href); + if (!e.target.checked) { + url.searchParams.set(DEFAULTS_URL_PARAM, 'off'); + } else { + url.searchParams.delete(DEFAULTS_URL_PARAM); + } + window.location.href = url.toString(); + }); +} + export default async function init(el) { el.innerHTML = '
'; - loadStyle(OST_STYLE_URL); + loadStyle(`${getMasLibsBase()}${OST_STYLE_URL}`); loadStyle('https://use.typekit.net/pps7abe.css'); const [ostEnv] = await Promise.all([ loadOstEnv(), - loadScript(OST_SCRIPT_URL), + loadScript(`${getMasLibsBase()}${OST_SCRIPT_URL}`), ]); function openOst() { @@ -281,6 +358,7 @@ export default async function init(el) { ...ostEnv, rootElement: el.firstElementChild, }); + addToggleSwitches(el, ostEnv); } if (ostEnv.aosAccessToken) { diff --git a/test/blocks/ost/ost.test.html.js b/test/blocks/ost/ost.test.html.js index cfe743f9d81..c7aeb93b40d 100644 --- a/test/blocks/ost/ost.test.html.js +++ b/test/blocks/ost/ost.test.html.js @@ -8,6 +8,8 @@ const perpM2M = { commitment: 'PERPETUAL', name: 'Stock', planType: 'M2M', + customer_segment: 'INDIVIDUAL', + market_segments: ['COM'], }; const defaults = { checkoutWorkflow: 'UCv3', @@ -46,6 +48,7 @@ function createLink(params = {}) { perpM2M, params, params.promo, + 'US', ); } @@ -244,14 +247,14 @@ describe('OST: merch link creation', () => { const type = types.checkoutUrl; it('with default params', async () => { - const link = createLink({ type }); + const link = await createLink({ type }); assertLink(link, perpM2M, { osi, type }); expect({ ...link.dataset }).to.eql({}); }); it('with promo and custom text', async () => { const ctaText = texts.try; - const link = createLink({ ctaText, promo, type }); + const link = await createLink({ ctaText, promo, type }); assertLink(link, perpM2M, { osi, promo, type }, ctaText); }); @@ -260,7 +263,7 @@ describe('OST: merch link creation', () => { const modal = 'd2p'; const entitlement = 'true'; const upgrade = 'false'; - const link = createLink({ ctaText, modal, entitlement, upgrade, type }); + const link = await createLink({ ctaText, modal, entitlement, upgrade, type }); assertLink(link, perpM2M, { osi, modal, entitlement, upgrade, type }, ctaText); }); }); @@ -269,15 +272,15 @@ describe('OST: merch link creation', () => { const type = types.price; it('with default params', async () => { - const link = createLink({ type }); + const link = await createLink({ type }); assertLink(link, perpM2M, { osi, type }); }); it('with default params from OST', async () => { - const link = createLink({ + const link = await createLink({ type, displayRecurrence: true, - displayPerUnit: true, + displayPerUnit: false, displayTax: false, displayOldPrice: false, forceTaxExclusive: false, @@ -290,7 +293,7 @@ describe('OST: merch link creation', () => { const displayPerUnit = true; const displayTax = true; const forceTaxExclusive = true; - const link = createLink({ + const link = await createLink({ displayRecurrence, displayPerUnit, displayTax,