From 1a8da2bb17e160091d63304fc10293f14d4a071e Mon Sep 17 00:00:00 2001 From: James Tsay Date: Mon, 16 Dec 2024 18:27:50 -0800 Subject: [PATCH 01/14] Use a locale map for prefetch --- acrobat/blocks/verb-widget/verb-widget.js | 96 ++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index 6d559290..f22ce9c8 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -32,6 +32,99 @@ const verbRedirMap = { 'chat-pdf-student': 'study', }; +const localeMap = { + '': 'us/en', + br: 'br/pt', + ca: 'ca/en', + ca_fr: 'ca/fr', + mx: 'mx/es', + la: 'la/es', + africa: 'africa/en', + za: 'za/en', + be_nl: 'be/nl', + be_fr: 'be/fr', + be_en: 'be/en', + cz: 'cz/cs', + cy_en: 'cy/en', + dk: 'dk/da', + de: 'de/de', + ee: 'ee/et', + es: 'es/es', + fr: 'fr/fr', + gr_en: 'gr/en', + gr_el: 'gr/el', + ie: 'ie/en', + il_en: 'il/en', + il_he: 'il/he', + it: 'it/it', + lv: 'lv/lv', + lt: 'lt/lt', + lu_de: 'lu/de', + lu_en: 'lu/en', + lu_fr: 'lu/fr', + hu: 'hu/hu', + mt: 'mt/en', + mena_en: 'mena/en', + mena_ar: 'mena/ar', + nl: 'nl/nl', + no: 'no/nb', + at: 'at/de', + pl: 'pl/pl', + pt: 'pt/pt', + ro: 'ro/ro', + ch_de: 'ch/de', + si: 'si/sl', + sk: 'sk/sk', + ch_fr: 'ch/fr', + fi: 'fi/fi', + se: 'se/sv', + ch_it: 'ch/it', + tr: 'tr/tr', + uk: 'gb/en', + bg: 'bg/bg', + ru: 'ru/ru', + ua: 'ua/uk', + au: 'au/en', + hk_en: 'hk/en', + in: 'in/en', + in_hi: 'in/hi', + nz: 'nz/en', + hk_zh: 'zh-hant-hk', + tw: 'zh-hant-tw', + jp: 'jp/ja', + kr: 'kr/ko', + ae_en: 'ae/en', + ae_ar: 'ae/ar', + sa_en: 'sa/en', + sa_ar: 'sa/ar', + th_en: 'th/en', + th_th: 'th/th', + sg: 'sg/en', + cl: 'cl/es', + co: 'co/es', + ar: 'ar/es', + cr: 'cr/es', + pr: 'pr/es', + ec: 'ec/es', + pe: 'pe/es', + eg_en: 'eg/en', + eg_ar: 'eg/ar', + gt: 'gt/es', + id_en: 'id/en', + id_id: 'id/id', + ph_en: 'ph/en', + ph_fil: 'ph/fil', + my_en: 'my/en', + my_ms: 'my/ms', + kw_en: 'kw/en', + kw_ar: 'kw/ar', + ng: 'ng/en', + qa_en: 'qa/en', + qa_ar: 'qa/ar', + vn_en: 'vn/en', + vn_vi: 'vn/vi', +}; + const setUser = () => { localStorage.setItem('unity.user', 'true'); }; @@ -43,9 +136,10 @@ const setDraggingClass = (widget, shouldToggle) => { function prefetchNextPage(verb) { const ENV = getEnv(); + const { locale } = getConfig(); const isProd = ENV === 'prod'; const nextPageHost = isProd ? 'acrobat.adobe.com' : 'stage.acrobat.adobe.com'; - const nextPageUrl = `https://${nextPageHost}/us/en/${verb}`; + const nextPageUrl = `https://${nextPageHost}/${localeMap[locale.prefix]}/${verb}`; const link = document.createElement('link'); link.rel = 'prefetch'; link.href = nextPageUrl; From 505f93054d275cfb27726815b9983e9ed0b97787 Mon Sep 17 00:00:00 2001 From: James Tsay Date: Tue, 17 Dec 2024 10:24:38 -0800 Subject: [PATCH 02/14] Use locale map in unity.js --- acrobat/blocks/unity/unity.js | 2 +- acrobat/blocks/verb-widget/verb-widget.js | 103 ++-------------------- 2 files changed, 7 insertions(+), 98 deletions(-) diff --git a/acrobat/blocks/unity/unity.js b/acrobat/blocks/unity/unity.js index b2d66d1a..0f60167d 100644 --- a/acrobat/blocks/unity/unity.js +++ b/acrobat/blocks/unity/unity.js @@ -1,6 +1,6 @@ import LIMITS from '../verb-widget/limits.js'; -const localeMap = { +export const localeMap = { '': 'en-us', br: 'pt-br', ca: 'en-ca', diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index f22ce9c8..d84259a5 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -2,6 +2,7 @@ import LIMITS from './limits.js'; import { setLibs, getEnv, isOldBrowser } from '../../scripts/utils.js'; import verbAnalytics from '../../scripts/alloy/verb-widget.js'; import createSvgElement from './icons.js'; +import { localeMap } from '../unity/unity.js'; const miloLibs = setLibs('/libs'); const { createTag, getConfig } = await import(`${miloLibs}/utils/utils.js`); @@ -32,99 +33,6 @@ const verbRedirMap = { 'chat-pdf-student': 'study', }; -const localeMap = { - '': 'us/en', - br: 'br/pt', - ca: 'ca/en', - ca_fr: 'ca/fr', - mx: 'mx/es', - la: 'la/es', - africa: 'africa/en', - za: 'za/en', - be_nl: 'be/nl', - be_fr: 'be/fr', - be_en: 'be/en', - cz: 'cz/cs', - cy_en: 'cy/en', - dk: 'dk/da', - de: 'de/de', - ee: 'ee/et', - es: 'es/es', - fr: 'fr/fr', - gr_en: 'gr/en', - gr_el: 'gr/el', - ie: 'ie/en', - il_en: 'il/en', - il_he: 'il/he', - it: 'it/it', - lv: 'lv/lv', - lt: 'lt/lt', - lu_de: 'lu/de', - lu_en: 'lu/en', - lu_fr: 'lu/fr', - hu: 'hu/hu', - mt: 'mt/en', - mena_en: 'mena/en', - mena_ar: 'mena/ar', - nl: 'nl/nl', - no: 'no/nb', - at: 'at/de', - pl: 'pl/pl', - pt: 'pt/pt', - ro: 'ro/ro', - ch_de: 'ch/de', - si: 'si/sl', - sk: 'sk/sk', - ch_fr: 'ch/fr', - fi: 'fi/fi', - se: 'se/sv', - ch_it: 'ch/it', - tr: 'tr/tr', - uk: 'gb/en', - bg: 'bg/bg', - ru: 'ru/ru', - ua: 'ua/uk', - au: 'au/en', - hk_en: 'hk/en', - in: 'in/en', - in_hi: 'in/hi', - nz: 'nz/en', - hk_zh: 'zh-hant-hk', - tw: 'zh-hant-tw', - jp: 'jp/ja', - kr: 'kr/ko', - ae_en: 'ae/en', - ae_ar: 'ae/ar', - sa_en: 'sa/en', - sa_ar: 'sa/ar', - th_en: 'th/en', - th_th: 'th/th', - sg: 'sg/en', - cl: 'cl/es', - co: 'co/es', - ar: 'ar/es', - cr: 'cr/es', - pr: 'pr/es', - ec: 'ec/es', - pe: 'pe/es', - eg_en: 'eg/en', - eg_ar: 'eg/ar', - gt: 'gt/es', - id_en: 'id/en', - id_id: 'id/id', - ph_en: 'ph/en', - ph_fil: 'ph/fil', - my_en: 'my/en', - my_ms: 'my/ms', - kw_en: 'kw/en', - kw_ar: 'kw/ar', - ng: 'ng/en', - qa_en: 'qa/en', - qa_ar: 'qa/ar', - vn_en: 'vn/en', - vn_vi: 'vn/vi', -}; - const setUser = () => { localStorage.setItem('unity.user', 'true'); }; @@ -135,16 +43,17 @@ const setDraggingClass = (widget, shouldToggle) => { }; function prefetchNextPage(verb) { - const ENV = getEnv(); const { locale } = getConfig(); - const isProd = ENV === 'prod'; - const nextPageHost = isProd ? 'acrobat.adobe.com' : 'stage.acrobat.adobe.com'; - const nextPageUrl = `https://${nextPageHost}/${localeMap[locale.prefix]}/${verb}`; + const localePath = localeMap[locale.prefix].split('-').reverse().join('/'); + const nextPageHost = getEnv() === 'prod' ? 'acrobat.adobe.com' : 'stage.acrobat.adobe.com'; + const nextPageUrl = `https://${nextPageHost}/${localePath}/${verb}`; + const link = document.createElement('link'); link.rel = 'prefetch'; link.href = nextPageUrl; link.crossOrigin = 'anonymous'; link.as = 'document'; + document.head.appendChild(link); } From 47cff9b240ef7924c2ebdb8e9117fc1c6fe11341 Mon Sep 17 00:00:00 2001 From: James Tsay Date: Tue, 17 Dec 2024 10:40:49 -0800 Subject: [PATCH 03/14] Fix locale.prefix --- acrobat/blocks/verb-widget/verb-widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index d84259a5..9a36104a 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -44,7 +44,7 @@ const setDraggingClass = (widget, shouldToggle) => { function prefetchNextPage(verb) { const { locale } = getConfig(); - const localePath = localeMap[locale.prefix].split('-').reverse().join('/'); + const localePath = localeMap[locale.prefix.replace('/', '')].split('-').reverse().join('/'); const nextPageHost = getEnv() === 'prod' ? 'acrobat.adobe.com' : 'stage.acrobat.adobe.com'; const nextPageUrl = `https://${nextPageHost}/${localePath}/${verb}`; From f2c55a8cc47a9704c1768febcd737ff8b84c269e Mon Sep 17 00:00:00 2001 From: James Tsay Date: Tue, 17 Dec 2024 15:31:03 -0800 Subject: [PATCH 04/14] Fix style for RTL --- acrobat/blocks/verb-widget/verb-widget.css | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css index b5c705c5..bc231528 100644 --- a/acrobat/blocks/verb-widget/verb-widget.css +++ b/acrobat/blocks/verb-widget/verb-widget.css @@ -26,7 +26,6 @@ font-size: 14px; line-height: 20px; max-width: 527px; - text-align: left; display: inline-block; color: #2c2c2c; } @@ -143,7 +142,6 @@ } .verb-copy { - text-align: left; font-size: 22px; font-weight: 400; line-height: 33px; @@ -236,7 +234,6 @@ .verb-heading { color: #2c2c2c; - text-align: left; font-weight: 600; font-size: 44px; margin: revert; @@ -254,7 +251,7 @@ font-size: 30px; font-weight: bold; color: black; - margin-left: 15px; + margin-inline-start: 15px; } .verb-mobile-cta { @@ -285,7 +282,7 @@ display: flex; flex: 1 1 55%; flex-direction: row; - padding: 48px 0 48px 48px; + padding: 48px; /* cursor: pointer; */ } From 741bf243390fc9c2059722f5219232e0dbf5fd69 Mon Sep 17 00:00:00 2001 From: James Tsay Date: Tue, 17 Dec 2024 15:44:37 -0800 Subject: [PATCH 05/14] Update CSS for RTL --- acrobat/blocks/verb-widget/verb-widget.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css index bc231528..04f6cec7 100644 --- a/acrobat/blocks/verb-widget/verb-widget.css +++ b/acrobat/blocks/verb-widget/verb-widget.css @@ -59,12 +59,14 @@ display: flex; width: 42px; height: 42px; - margin: 15px 3px 0 0; + margin: 15px 0 0; + margin-inline-end: 3px; } .info-icon { display: flex; - margin: 15px 0 0 20px; + margin: 15px 0 0; + margin-inline-start: 20px; } .verb-footer { From 954e73911bb1dcdd10e8d199a549b222baabde6d Mon Sep 17 00:00:00 2001 From: James Tsay Date: Tue, 17 Dec 2024 16:21:49 -0800 Subject: [PATCH 06/14] Fix CSS for RTL --- acrobat/blocks/verb-widget/verb-widget.css | 1 - 1 file changed, 1 deletion(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.css b/acrobat/blocks/verb-widget/verb-widget.css index 04f6cec7..d9c0c61d 100644 --- a/acrobat/blocks/verb-widget/verb-widget.css +++ b/acrobat/blocks/verb-widget/verb-widget.css @@ -70,7 +70,6 @@ } .verb-footer { - text-align: center; align-items: center; display: flex; flex-flow: row; From 22616fb7e9458e2dca8fed73b29a3c3cfd105b37 Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Fri, 6 Dec 2024 15:44:32 +0800 Subject: [PATCH 07/14] UI, Placeholders, and first modal behaviors --- acrobat/blocks/mobile-widget/mobile-widget.js | 151 +++++++++++++++++- acrobat/scripts/alloy/mobile-widget-shown.js | 9 +- acrobat/scripts/alloy/mobile-widget.js | 9 +- acrobat/styles/styles.css | 50 ++++++ 4 files changed, 205 insertions(+), 14 deletions(-) diff --git a/acrobat/blocks/mobile-widget/mobile-widget.js b/acrobat/blocks/mobile-widget/mobile-widget.js index 72bfdf75..c12af7b0 100644 --- a/acrobat/blocks/mobile-widget/mobile-widget.js +++ b/acrobat/blocks/mobile-widget/mobile-widget.js @@ -1,6 +1,8 @@ import mobileAnalytics from '../../scripts/alloy/mobile-widget.js'; import mobileAnalyticsShown from '../../scripts/alloy/mobile-widget-shown.js'; -import { getEnv, isOldBrowser } from '../../scripts/utils.js'; +import { setLibs, getEnv, isOldBrowser } from '../../scripts/utils.js'; + +const miloLibs = setLibs('/libs'); const verbRedirMap = { createpdf: 'createpdf', @@ -32,6 +34,7 @@ const verbRedirMapAnalytics = { const EOLBrowserPage = 'https://acrobat.adobe.com/home/index-browser-eol.html'; const fallBack = 'https://www.adobe.com/go/acrobat-overview'; +// Redirect Logic function redDir(verb) { if (isOldBrowser()) { window.location.href = EOLBrowserPage; @@ -49,6 +52,7 @@ function redDir(verb) { window.location.href = newLocation; } +// Helper to Create HTML Tags function createTag(tag, attributes, html) { const el = document.createElement(tag); if (html) { @@ -62,8 +66,17 @@ function createTag(tag, attributes, html) { return el; } +let aaVerbName = ''; +let appLink = ''; +let modalCtaLink = ''; +let externalLinkHref = null; +let isAcrobatExitOpen = false; +let inactivityTimer; +const inactivityInterval = 20000; + +// Mobile Widget Creation function createMobileWidget(element, content, verb) { - const aaVerbName = `${verbRedirMapAnalytics[verb] || verb}`; + aaVerbName = `${verbRedirMapAnalytics[verb] || verb}`; const artID = content[1].querySelector('a')?.href || content[1].querySelector('img')?.src; const wrapper = createTag('div', { class: 'mobile-widget_wrapper' }); @@ -78,7 +91,6 @@ function createMobileWidget(element, content, verb) { const artworkWrapper = createTag('div', { class: 'mobile-widget_artwork-wrapper' }); const copy = createTag('div', { class: 'mobile-widget_copy' }, content[2].textContent); - let appLink = ''; if (/iPad|iPhone|iPod/.test(window?.browser?.ua) && !window.MSStream) { appLink = content[4].textContent.toString().trim(); } else if (/android/i.test(window?.browser?.ua)) { @@ -87,7 +99,7 @@ function createMobileWidget(element, content, verb) { appLink = content[4].textContent.toString().trim(); } - const mobileCta = createTag('a', { class: 'mobile-widget_cta', href: appLink }, content[3].textContent); + const mobileCta = createTag('a', { class: 'mobile-widget_cta', href: appLink, target: '_blank' }, content[3].textContent); const ctaWrapper = createTag('div', { class: 'mobile-widget_cta-wrapper' }); titleWrapper.append(titleImg, title); @@ -106,16 +118,143 @@ function createMobileWidget(element, content, verb) { }); } +window.addEventListener('milo:modal:closed', () => { + if (!isAcrobatExitOpen) return; + mobileAnalytics(aaVerbName, 'acrobat-exit-modal:closed'); + isAcrobatExitOpen = false; + sessionStorage.setItem('modalDismissed', 'true'); + if (externalLinkHref) { + window.location.href = externalLinkHref; + externalLinkHref = ''; + } +}); + +const getPlaceHolders = async () => { + const { getConfig } = await import(`${miloLibs}/utils/utils.js`); + const config = await getConfig(); + + if (!Object.keys(window.mph || {}).length) { + const placeholdersPath = `${config.locale.contentRoot}/placeholders.json`; + try { + const response = await fetch(placeholdersPath); + if (response.ok) { + const placeholderData = await response.json(); + placeholderData.data.forEach(({ key, value }) => { + window.mph[key] = value.replace(/\u00A0/g, ' '); + }); + } + } catch (error) { + window.lana?.log(`Failed to load placeholders: ${error?.message}`); + } + } +}; + +async function showModal(event = 'shown') { + await getPlaceHolders(); + const { getModal } = await import(`${miloLibs}/blocks/modal/modal.js`); + const acrobatIcon = createTag('img', { class: 'modal-icon', src: '/acrobat/img/icons/widget-icon.png' }); + const acrobatLabel = createTag('p', { class: 'modal-label' }, 'Adobe Acrobat'); + const acrobatLogo = createTag('div', { class: 'modal-logo' }); + acrobatLogo.append(acrobatIcon, acrobatLabel); + const modalTitle = createTag('h2', { class: 'modal-title' }, (window.mph && window.mph['acrobat-exit-modal-title']) || 'Before you go, get the free mobile app.'); + const modalMessage = createTag('p', { class: 'modal-message' }, (window.mph && window.mph['acrobat-exit-modal-message']) || 'Quickly comment, sign, and share PDFs — all from your phone.'); + const modalCta = createTag('a', { class: 'modal-cta', href: (window.mph && window.mph['acrobat-exit-modal-cta-url']) || '/acrobat/mobile-app.html' }, (window.mph && window.mph['acrobat-exit-modal-cta-label']) || 'Download app'); + modalCtaLink = modalCta.href; + const modalContent = createTag('div', { class: 'modal-content' }); + modalContent.append(acrobatLogo, modalTitle, modalMessage, modalCta); + isAcrobatExitOpen = true; + await getModal(null, { + id: 'acrobat-exit', + content: modalContent, + closeEvent: 'Closed', + class: 'acrobat-exit', + }); + mobileAnalyticsShown(aaVerbName, `acrobat-exit-modal:shown-${event}`); +} + +const internalDomains = [ + 'adobe.com', + 'www.adobe.com', + 'www.stage.adobe.com', +]; + +const internalPatterns = [ + '**--dc--adobecom.hlx.page', + '**--dc--adobecom.hlx.live', +]; + +function isInternalDomain(hostname) { + if (internalDomains.includes(hostname)) return true; + return internalPatterns.some((pattern) => hostname.includes(pattern)); +} + +function isExternalLink(link) { + // eslint-disable-next-line compat/compat + const linkHost = new URL(link.href).hostname; + return !( + linkHost === window.location.hostname + || isInternalDomain(linkHost) + ); +} + +function shouldOpenModal(link) { + return ( + link && link.href !== appLink && link.href !== modalCtaLink && isExternalLink(link) + ); +} + +document.addEventListener('click', (event) => { + const link = event.target.closest('a'); + if (shouldOpenModal(link)) { + event.preventDefault(); + externalLinkHref = link.href; + if (!isAcrobatExitOpen) { + showModal('external-link'); + } else { + window.location.href = link.href; + } + } +}); + +window.addEventListener('beforeunload', async (event) => { + if (sessionStorage.getItem('modalDismissed') === 'true') { + return; + } + event.preventDefault(); + event.returnValue = ''; + if (!isAcrobatExitOpen) { + await showModal('before-unload'); + } +}); + export default async function init(element) { element.closest('main > div').dataset.section = 'widget'; const content = Array.from(element.querySelectorAll(':scope > div')); const VERB = element.dataset.verb; content.forEach((con) => con.classList.add('hide')); - createMobileWidget(element, content, VERB); - // Listen for the IMS:Ready event and call redDir if user is signed in + + if (!element.querySelector('.mobile-widget_wrapper')) { + createMobileWidget(element, content, VERB); + } + window.addEventListener('IMS:Ready', async () => { if (window.adobeIMS.isSignedInUser()) { redDir(VERB); } }); } + +function resetInactivityTimer() { + clearTimeout(inactivityTimer); + inactivityTimer = setTimeout(async () => { + if (!sessionStorage.getItem('modalDismissed') && !isAcrobatExitOpen) { + await showModal('idle'); + } + }, inactivityInterval); +} + +['mousemove', 'click', 'keypress', 'scroll', 'touchstart'].forEach((event) => { + document.addEventListener(event, resetInactivityTimer); +}); + +resetInactivityTimer(); diff --git a/acrobat/scripts/alloy/mobile-widget-shown.js b/acrobat/scripts/alloy/mobile-widget-shown.js index d2b37eef..a6f9644f 100644 --- a/acrobat/scripts/alloy/mobile-widget-shown.js +++ b/acrobat/scripts/alloy/mobile-widget-shown.js @@ -23,7 +23,8 @@ if (params.dropzone2) { appTags.push('dropzone2'); } -export default function init(verb) { +export default function init(verb, type = 'landing:shown') { + const eventName = `acrobat:verb-${verb}:${type}`; const event = { documentUnloading: true, data: { @@ -32,14 +33,14 @@ export default function init(verb) { webInteraction: { linkClicks: { value: 1 }, type: 'other', - name: `acrobat:verb-${verb}:landing:shown`, + name: eventName, }, }, _adobe_corpnew: { digitalData: { - dcweb: { event: { pagename: `acrobat:verb-${verb}:landing:shown` } }, + dcweb: { event: { pagename: eventName } }, dcweb2: { - event: { pagename: `acrobat:verb-${verb}:landing:shown` }, + event: { pagename: eventName }, source: { user_agent: navigator.userAgent, lang: document.documentElement.lang, diff --git a/acrobat/scripts/alloy/mobile-widget.js b/acrobat/scripts/alloy/mobile-widget.js index 2894874a..f398fe48 100644 --- a/acrobat/scripts/alloy/mobile-widget.js +++ b/acrobat/scripts/alloy/mobile-widget.js @@ -23,7 +23,8 @@ if (params.dropzone2) { appTags.push('dropzone2'); } -export default function init(verb) { +export default function init(verb, type = 'goto-app:clicked') { + const eventName = `acrobat:verb-${verb}:${type}`; const event = { documentUnloading: true, data: { @@ -32,14 +33,14 @@ export default function init(verb) { webInteraction: { linkClicks: { value: 1 }, type: 'other', - name: `acrobat:verb-${verb}:goto-app:clicked`, + name: eventName, }, }, _adobe_corpnew: { digitalData: { - dcweb: { event: { pagename: `acrobat:verb-${verb}:goto-app:clicked` } }, + dcweb: { event: { pagename: eventName } }, dcweb2: { - event: { pagename: `acrobat:verb-${verb}:goto-app:clicked` }, + event: { pagename: eventName }, source: { user_agent: navigator.userAgent, lang: document.documentElement.lang, diff --git a/acrobat/styles/styles.css b/acrobat/styles/styles.css index ded8c54b..ce595e78 100644 --- a/acrobat/styles/styles.css +++ b/acrobat/styles/styles.css @@ -299,6 +299,56 @@ div.how-to ol li::before{ margin-top: 10px; } +.dialog-modal.acrobat-exit .modal-content { + padding: 40px 24px; +} + +.dialog-modal.acrobat-exit .modal-content .modal-logo { + display: flex; + flex-direction: row; + align-items: center; + height: 40px; +} + +.modal-logo .modal-icon { + width: 40px; + height: auto; +} + +.modal-logo .modal-label { + font-weight: 700; + line-height: 20px; + text-align: left; + text-underline-position: from-font; + text-decoration-skip-ink: none; + margin-left: 16px; +} + +.modal-content .modal-title { + margin-block-start: 24px; + margin-block-end: 16px; +} + +.modal-content .modal-message { + margin-block-start: 16px; +} + +.modal-content .modal-cta { + font-size: 15px; + font-weight: 700; + line-height: 19px; + border: solid 2px #0265dc; + background-color: #0265dc; + border-radius: 24px; + text-align: center; + color: white; + text-decoration: none; + padding: 5px 18px; + display: inline-flex; + align-items: center; + justify-content: center; +} + .dialog-modal.extension-modal.backdrop-off { width: 30rem; max-height: 188px; From 242c4af43f2f8e8b6492085d6e2a2dc25349d222 Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Wed, 18 Dec 2024 12:36:15 -0500 Subject: [PATCH 08/14] Avoiding Repeatedly checking on mph --- acrobat/blocks/mobile-widget/mobile-widget.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/acrobat/blocks/mobile-widget/mobile-widget.js b/acrobat/blocks/mobile-widget/mobile-widget.js index c12af7b0..52fcae91 100644 --- a/acrobat/blocks/mobile-widget/mobile-widget.js +++ b/acrobat/blocks/mobile-widget/mobile-widget.js @@ -150,15 +150,16 @@ const getPlaceHolders = async () => { }; async function showModal(event = 'shown') { + const mph = window.mph || {}; await getPlaceHolders(); const { getModal } = await import(`${miloLibs}/blocks/modal/modal.js`); const acrobatIcon = createTag('img', { class: 'modal-icon', src: '/acrobat/img/icons/widget-icon.png' }); const acrobatLabel = createTag('p', { class: 'modal-label' }, 'Adobe Acrobat'); const acrobatLogo = createTag('div', { class: 'modal-logo' }); acrobatLogo.append(acrobatIcon, acrobatLabel); - const modalTitle = createTag('h2', { class: 'modal-title' }, (window.mph && window.mph['acrobat-exit-modal-title']) || 'Before you go, get the free mobile app.'); - const modalMessage = createTag('p', { class: 'modal-message' }, (window.mph && window.mph['acrobat-exit-modal-message']) || 'Quickly comment, sign, and share PDFs — all from your phone.'); - const modalCta = createTag('a', { class: 'modal-cta', href: (window.mph && window.mph['acrobat-exit-modal-cta-url']) || '/acrobat/mobile-app.html' }, (window.mph && window.mph['acrobat-exit-modal-cta-label']) || 'Download app'); + const modalTitle = createTag('h2', { class: 'modal-title' }, mph['acrobat-exit-modal-title'] || 'Before you go, get the free mobile app.'); + const modalMessage = createTag('p', { class: 'modal-message' }, mph['acrobat-exit-modal-message'] || 'Quickly comment, sign, and share PDFs — all from your phone.'); + const modalCta = createTag('a', { class: 'modal-cta', href: mph['acrobat-exit-modal-cta-url'] || '/acrobat/mobile-app.html' }, mph['acrobat-exit-modal-cta-label'] || 'Download app'); modalCtaLink = modalCta.href; const modalContent = createTag('div', { class: 'modal-content' }); modalContent.append(acrobatLogo, modalTitle, modalMessage, modalCta); From 7433572cf54ce1b23c59c5846aeb397ae40e7e2a Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Thu, 12 Dec 2024 20:06:46 -0800 Subject: [PATCH 09/14] Adding multiple attribute and increasing max file size in compress pdf --- acrobat/blocks/verb-widget/limits.js | 26 +++++++++++++++++------ acrobat/blocks/verb-widget/verb-widget.js | 9 +++++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/acrobat/blocks/verb-widget/limits.js b/acrobat/blocks/verb-widget/limits.js index 4457b30b..f9436f94 100644 --- a/acrobat/blocks/verb-widget/limits.js +++ b/acrobat/blocks/verb-widget/limits.js @@ -1,25 +1,37 @@ const LIMITS = { fillsign: { - maxFileSize: 100000000, // 100 MB + maxFileSize: 104857600, // 100 MB maxFileSizeFriendly: '100 MB', // 100 MB - acceptedFiles: '.pdf', + acceptedFiles: ['application/pdf'], maxNumFiles: 1, + multipleFiles: false, mobileApp: true, }, 'delete-pages': { maxFileSize: 100000000, // 1 MB - acceptedFiles: '.pdf', + acceptedFiles: ['application/pdf'], maxNumFiles: 1, }, 'number-pages': { maxFileSize: 100000000, // 1 MB - acceptedFiles: '.pdf', + acceptedFiles: ['application/pdf'], maxNumFiles: 1, }, 'compress-pdf': { - maxFileSize: 100000000, - acceptedFiles: '.pdf', - maxNumFiles: 1, + maxFileSize: 2147483648, + maxFileSizeFriendly: '2 GB', + acceptedFiles: [ + 'application/pdf', + 'application/msword', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'application/vnd.ms-powerpoint', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'application/vnd.ms-excel', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'image/jpeg', + 'image/png', + ], + multipleFiles: true, }, }; diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index 6d559290..dbc69271 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -130,7 +130,14 @@ export default async function init(element) { } const widgetMobileButton = createTag('a', { class: 'verb-mobile-cta', href: mobileLink }, window.mph['verb-widget-cta-mobile']); - const button = createTag('input', { type: 'file', accept: LIMITS[VERB].acceptedFiles, id: 'file-upload', class: 'hide', 'aria-hidden': true }); + const button = createTag('input', { + type: 'file', + accept: LIMITS[VERB]?.acceptedFiles, + id: 'file-upload', + class: 'hide', + 'aria-hidden': true, + ...(LIMITS[VERB]?.multipleFiles && { multiple: '' }), + }); const widgetImage = createTag('div', { class: 'verb-image' }); const verbIconName = `${VERB}`; const verbImageSvg = createSvgElement(verbIconName); From 7e759eec61d6720d2c9c73f08318fd2babb14df9 Mon Sep 17 00:00:00 2001 From: Ruchika Sinha <69535463+Ruchika4@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:41:57 -0800 Subject: [PATCH 10/14] Update unity.js --- acrobat/blocks/unity/unity.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/acrobat/blocks/unity/unity.js b/acrobat/blocks/unity/unity.js index b2d66d1a..8a362d7c 100644 --- a/acrobat/blocks/unity/unity.js +++ b/acrobat/blocks/unity/unity.js @@ -57,8 +57,8 @@ const localeMap = { in: 'en-in', in_hi: 'hi-in', nz: 'en-nz', - hk_zh: 'zh-hant-hk', - tw: 'zh-hant-tw', + hk_zh: 'zh-tw', + tw: 'zh-tw', jp: 'ja-jp', kr: 'ko-kr', ae_en: 'en-ae', From 9dddfc2b44e7dbcb10db4b1def5922664982a587 Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Thu, 19 Dec 2024 12:48:45 -0500 Subject: [PATCH 11/14] Compress PDF Icon --- acrobat/blocks/verb-widget/icons.js | 1 + 1 file changed, 1 insertion(+) diff --git a/acrobat/blocks/verb-widget/icons.js b/acrobat/blocks/verb-widget/icons.js index bf90dfc1..460dbbb7 100644 --- a/acrobat/blocks/verb-widget/icons.js +++ b/acrobat/blocks/verb-widget/icons.js @@ -4,6 +4,7 @@ const ICONS = { WIDGET_ICON: '', fillsign: '', + 'compress-pdf': '', UPLOAD_ICON: '', SECURITY_ICON: '', INFO_ICON: '', From 75ad33f99c4048da96dc73bf64246cd2e6c09c7a Mon Sep 17 00:00:00 2001 From: James Tsay <65299136+TsayAdobe@users.noreply.github.com> Date: Thu, 19 Dec 2024 21:40:16 -0800 Subject: [PATCH 12/14] Smoke E2E Test for Sign-PDF (#923) * Smoke test for sign-pdf --- package-lock.json | 8 +-- package.json | 2 +- test/e2e/features/unity/verbs.feature | 26 ++++++++ test/e2e/page-objects/unity.page.js | 34 ++++++++++ test/e2e/page-objects/verbwidget.section.js | 69 +++++++++++++++++++++ test/e2e/step-definitions/dc.steps.js | 43 +++++++++++++ 6 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 test/e2e/features/unity/verbs.feature create mode 100644 test/e2e/page-objects/unity.page.js create mode 100644 test/e2e/page-objects/verbwidget.section.js diff --git a/package-lock.json b/package-lock.json index 929bcedd..f7b27588 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ }, "devDependencies": { "@amwp/platform-ui-automation": "^0.0.6", - "@amwp/platform-ui-lib-adobe": "^0.0.7", + "@amwp/platform-ui-lib-adobe": "^0.0.9", "@babel/core": "7.23.2", "@babel/eslint-parser": "7.22.15", "@babel/register": "7.22.15", @@ -119,9 +119,9 @@ } }, "node_modules/@amwp/platform-ui-lib-adobe": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@amwp/platform-ui-lib-adobe/-/platform-ui-lib-adobe-0.0.7.tgz", - "integrity": "sha512-5tF6QFLZIvGDS/thYanj2srt6NrSQ0lq34m9KzaPEIVfj2OywLLVoavDaxgJvmhTGywIGngMrXyDx+YIUIInyA==", + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@amwp/platform-ui-lib-adobe/-/platform-ui-lib-adobe-0.0.9.tgz", + "integrity": "sha512-F/1En1XlUJMYxbpesvd0G64XDyO8xsLUGIyyoBs4YehcDVk7CbHRHVShs6U9LoQ7A4ElEb5KXK7W5YqtRWp+nw==", "dev": true }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 3b3bd3c9..f4ce8a6e 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "homepage": "https://github.com/adobecom/dc#readme", "devDependencies": { "@amwp/platform-ui-automation": "^0.0.6", - "@amwp/platform-ui-lib-adobe": "^0.0.7", + "@amwp/platform-ui-lib-adobe": "^0.0.9", "@babel/core": "7.23.2", "@babel/eslint-parser": "7.22.15", "@babel/register": "7.22.15", diff --git a/test/e2e/features/unity/verbs.feature b/test/e2e/features/unity/verbs.feature new file mode 100644 index 00000000..2e5db0ce --- /dev/null +++ b/test/e2e/features/unity/verbs.feature @@ -0,0 +1,26 @@ +Feature: Frictionless Converter Block + + Background: + Given I have a new browser context + + @smoke @unity @sign-pdf @choosefile + Scenario Outline: L1 Verb - Upload and sign-in + Given I go to the page + Then I choose the file "" to upload + Then I wait for 5 seconds + Then I should see the address bar contains "acrobat.adobe.com" + + Examples: + | Verb | File | + | sign-pdf | test-files/test.pdf | + + @smoke @unity @sign-pdf @dragndrop + Scenario Outline: L1 Verb - Upload and sign-in + Given I go to the page + Then I drag-and-drop the file "" to upload + Then I wait for 5 seconds + Then I should see the address bar contains "acrobat.adobe.com" + + Examples: + | Verb | File | + | sign-pdf | test-files/test.pdf | \ No newline at end of file diff --git a/test/e2e/page-objects/unity.page.js b/test/e2e/page-objects/unity.page.js new file mode 100644 index 00000000..31ebbe71 --- /dev/null +++ b/test/e2e/page-objects/unity.page.js @@ -0,0 +1,34 @@ +import { classes } from "polytype"; +import { DcGnavPage } from "./dcgnav.page"; +import { CaaSSection } from "./caas.section"; +import { VerbWidgetSection } from "./verbwidget.section"; + +export class UnityPage extends classes(DcGnavPage, VerbWidgetSection, CaaSSection) { + constructor(contentPath) { + super({ + super: DcGnavPage, + arguments: [contentPath], + }); + this.buildProps({ + howToDefault: 'div[data-path*="how-to/default"]', + howTo2ndConversion: '[data-tag="2nd conversion"] div[data-path*="how-to/2nd-conversion"]', + verbSubfooter: '.verb-subfooter', + reviewComponent: '.review', + reviewStats: '.hlx-ReviewStats', + reviewSubmitResponse: '.hlx-submitResponse', + reviewDisabled: '.hlx-Review-ratingFields[disabled]', + reviewCommentField: '.hlx-Review-commentFields textarea', + reviewCommentSubmit: '.hlx-Review-commentFields input[type="submit"]', + reviewInputField: 'fieldset.hlx-Review-ratingFields input', + signUp: '[href*="https://auth.services.adobe.com"][href*="signup"]', + extensionModal: '#chromeext, #edgeext', + closeExtensionModal: '#chromeext .dialog-close, #edgeext .dialog-close', + eventwrapperOnload: '.eventwrapper.onload', + previewDescription: 'div[class*="previewDescription"]', + }); + } + + reviewStartInput(rating) { + return this.native.locator(`.hlx-Review-ratingFields input[value="${rating}"]`); + } +} diff --git a/test/e2e/page-objects/verbwidget.section.js b/test/e2e/page-objects/verbwidget.section.js new file mode 100644 index 00000000..8cfeee3f --- /dev/null +++ b/test/e2e/page-objects/verbwidget.section.js @@ -0,0 +1,69 @@ +import fs from 'fs'; +import path from 'path'; +import { Section } from '@amwp/platform-ui-automation/lib/common/page-objects/section'; + +export class VerbWidgetSection extends Section { + constructor() { + super(); + this.buildProps({ + selectButton: '.verb-cta', + fileUploadInput: '#file-upload', + dropZone: '#drop-zone', + }) + } + + async chooseFiles(filePaths) { + const fileChooserPromise = this.native.waitForEvent('filechooser'); + this.selectButton.click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(filePaths); + } + + async dragndropFiles(filePaths) { + const filePath = filePaths[0]; + const buffer = fs.readFileSync(filePath).toString('base64'); + const basename = path.basename(filePath); + + const dataTransfer = await this.dropZone.evaluateHandle(async({bufferData, basename}) => { + const dt = new DataTransfer(); + const blobData = await fetch(bufferData).then((res) => res.blob()); + const file = new File([blobData], basename, { type: 'application/pdf' }); + dt.items.add(file); + return dt; + }, { + bufferData: `data:application/octet-stream;base64,${buffer}`, + basename + }); + + await this.dropZone.dispatchEvent('drop', { dataTransfer }); + } +} + +async function dragAndDropFile( + page, + selector, + filePath, + fileName, + fileType = '' + ) { + const buffer = readFileSync(filePath).toString('base64'); + + const dataTransfer = await page.evaluateHandle( + async ({ bufferData, localFileName, localFileType }) => { + const dt = new DataTransfer(); + + const blobData = await fetch(bufferData).then((res) => res.blob()); + + const file = new File([blobData], localFileName, { type: localFileType }); + dt.items.add(file); + return dt; + }, + { + bufferData: `data:application/octet-stream;base64,${buffer}`, + localFileName: fileName, + localFileType: fileType, + } + ); + + await page.dispatchEvent(selector, 'drop', { dataTransfer }); + }; \ No newline at end of file diff --git a/test/e2e/step-definitions/dc.steps.js b/test/e2e/step-definitions/dc.steps.js index a42ac101..8fe20c8e 100644 --- a/test/e2e/step-definitions/dc.steps.js +++ b/test/e2e/step-definitions/dc.steps.js @@ -23,6 +23,7 @@ import { MergePdfPage } from "../page-objects/mergepdf.page"; import { CompressPdfPage } from "../page-objects/compresspdf.page"; import { PasswordProtectPdfPage } from "../page-objects/passwordprotectpdf.page"; import { FrictionlessPage } from "../page-objects/frictionless.page"; +import { UnityPage } from "../page-objects/unity.page"; import { DCPage } from "../page-objects/dc.page"; import { cardinal } from "../support/cardinal"; import { expect } from "@playwright/test"; @@ -643,3 +644,45 @@ Then(/^I confirm phone number is different and has geo-ip value "([^"]*)"$/, asy expect(this.phoneNumber).not.toEqual(geoIpPhoneNumber); expect(geoIpPhoneNumber).toEqual(geoIpPhoneNumberValue); }) + +Then(/^I choose the (?:PDF|file|files) "([^\"]*)" to upload$/, async function (filePath) { + this.context(UnityPage); + const filePaths = filePath.split(","); + const absPaths = filePaths.map((x) => + path.resolve(global.config.profile.site, x) + ); + let retry = 3; + while (retry > 0) { + await expect(this.page.selectButton).toHaveCount(1, { timeout: 15000 }); + await this.page.native.waitForTimeout(2000); + try { + await this.page.chooseFiles(absPaths); + await this.page.native.waitForTimeout(2000); + await expect(this.page.selectButton).toHaveCount(0, { timeout: 15000 }); + retry = 0; + } catch { + retry--; + } + } +}); + +Then(/^I drag-and-drop the (?:PDF|file|files) "([^\"]*)" to upload$/, async function (filePath) { + this.context(UnityPage); + const filePaths = filePath.split(","); + const absPaths = filePaths.map((x) => + path.resolve(global.config.profile.site, x) + ); + let retry = 3; + while (retry > 0) { + await expect(this.page.selectButton).toHaveCount(1, { timeout: 15000 }); + await this.page.native.waitForTimeout(2000); + try { + await this.page.dragndropFiles(absPaths); + await this.page.native.waitForTimeout(2000); + await expect(this.page.selectButton).toHaveCount(0, { timeout: 15000 }); + retry = 0; + } catch { + retry--; + } + } +}); \ No newline at end of file From c204977832aa9adcd75e7431d8d4ad7eaa070ebd Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Thu, 19 Dec 2024 19:20:29 -0500 Subject: [PATCH 13/14] Removing waiting a second to send analytics and adding lana logs on done --- acrobat/blocks/verb-widget/verb-widget.js | 2 +- acrobat/scripts/alloy/verb-widget.js | 33 ++++++++++++++--------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/acrobat/blocks/verb-widget/verb-widget.js b/acrobat/blocks/verb-widget/verb-widget.js index 9a36104a..297bda92 100644 --- a/acrobat/blocks/verb-widget/verb-widget.js +++ b/acrobat/blocks/verb-widget/verb-widget.js @@ -270,7 +270,7 @@ export default async function init(element) { } if (e.detail?.event === 'uploaded') { - verbAnalytics('job:test-uploaded', VERB, e.detail?.data); + verbAnalytics('job:test-uploaded', VERB, e.detail?.data, false); exitFlag = true; setUser(); document.cookie = `UTS_Uploaded=${Date.now()};domain=.adobe.com;path=/;expires=${cookieExp}`; diff --git a/acrobat/scripts/alloy/verb-widget.js b/acrobat/scripts/alloy/verb-widget.js index a17720c7..435d9a7d 100644 --- a/acrobat/scripts/alloy/verb-widget.js +++ b/acrobat/scripts/alloy/verb-widget.js @@ -23,7 +23,7 @@ if (params.dropzone2) { appTags.push('dropzone2'); } -export default function init(eventName, verb, metaData) { +export default function init(eventName, verb, metaData, documentUnloading = true) { function getSessionID() { const aToken = window.adobeIMS.getAccessToken(); const arrayToken = aToken?.token.split('.'); @@ -33,7 +33,25 @@ export default function init(eventName, verb, metaData) { return tokenPayload.sub || tokenPayload.user_id; } const event = { - documentUnloading: true, + documentUnloading, + // eslint-disable-next-line + done: function (AJOPropositionResult, error) { + if (!documentUnloading) { + const accountType = window?.adobeIMS?.getAccountType(); + const verbEvent = `acrobat:verb-${verb}:${eventName}`; + if (error) { + window.lana?.log( + `Error Code: ${error}, Status: 'Unknown', Message: An error occurred while sending ${verbEvent}, Account Type: ${accountType}`, + { sampleRate: 100, tags: 'DC_Milo,Project Unity (DC)' }, + ); + } else { + window.lana?.log( + `Message: Event ${verbEvent} has been sent successfully, Account Type: ${accountType}`, + { sampleRate: 100, tags: 'DC_Milo,Project Unity (DC)' }, + ); + } + } + }, data: { eventType: 'web.webinteraction.linkClicks', web: { @@ -104,15 +122,6 @@ export default function init(eventName, verb, metaData) { }, }, }; - - // Alloy Ready... - const AlloyReady = setInterval(() => { - // eslint-disable-next-line no-underscore-dangle - if (window?._satellite?.track) { - clearInterval(AlloyReady); - // eslint-disable-next-line no-underscore-dangle - window._satellite?.track('event', event); - } - }, 1000); // eslint-disable-next-line no-underscore-dangle + window._satellite?.track?.('event', event); } From d6ff62a78c3bea94e199f643961a68085d9b72ff Mon Sep 17 00:00:00 2001 From: Joaquin Rivero Date: Mon, 23 Dec 2024 11:36:19 -0600 Subject: [PATCH 14/14] Adding aem domains & test --- acrobat/blocks/mobile-widget/mobile-widget.js | 2 ++ test/blocks/mobile-widget/mobile-widget.test.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/acrobat/blocks/mobile-widget/mobile-widget.js b/acrobat/blocks/mobile-widget/mobile-widget.js index 52fcae91..5675d373 100644 --- a/acrobat/blocks/mobile-widget/mobile-widget.js +++ b/acrobat/blocks/mobile-widget/mobile-widget.js @@ -180,6 +180,8 @@ const internalDomains = [ ]; const internalPatterns = [ + '**--dc--adobecom.aem.page', + '**--dc--adobecom.aem.live', '**--dc--adobecom.hlx.page', '**--dc--adobecom.hlx.live', ]; diff --git a/test/blocks/mobile-widget/mobile-widget.test.js b/test/blocks/mobile-widget/mobile-widget.test.js index 7ba3b5c5..677ea20b 100644 --- a/test/blocks/mobile-widget/mobile-widget.test.js +++ b/test/blocks/mobile-widget/mobile-widget.test.js @@ -3,6 +3,8 @@ import { expect } from '@esm-bundle/chai'; const { default: init } = await import('../../../acrobat/blocks/mobile-widget/mobile-widget.js'); +sessionStorage.setItem('modalDismissed', 'true'); + describe('Mobile widget', () => { it('is complete', async () => { document.body.innerHTML = await readFile({ path: './mocks/body.html' });