From f57f7c61464ee8b6c4e2f8f7e4a40d88f142e371 Mon Sep 17 00:00:00 2001 From: Ruchika Sinha <69535463+Ruchika4@users.noreply.github.com> Date: Mon, 4 Mar 2024 07:47:12 -0800 Subject: [PATCH] Sync main with stage (#198) * universal promo terms 1st commit * Added API failure case handler * Remove console.log and updated the failure case. * nit: typo * Setting default api_key * nit: TODO * icon / other original content support, css to hide header/footer completely. * poc reviewd * api key updated * nit: eslint update * nit: eslint update * nit: eslint update * nit: eslint update * nit: eslint update * nit: eslint update * test added * added test * MWPW-142280 Firefly single feature marquee (#191) * firefly single feature * fix lint issue * fix blinking cursor issue on mobile and tablet * optimize --------- Co-authored-by: Ruchika Sinha * MWPW-143215 [sidenav] make re-ordering possible with camel case (#192) * service_providers is now optional as api_key * update test * MWPW-143533-Handle css for 2 values in selector tray (#195) * MWPW-143533 * fix issues --------- Co-authored-by: Ruchika Sinha * [Universal promo terms] update env logic (#197) * stage condition added for env * check prod URL instead --------- Co-authored-by: Sean Choi Co-authored-by: Ruchika Sinha Co-authored-by: Nicolas Peltier <1032754+npeltier@users.noreply.github.com> --- creativecloud/blocks/sidenav/sidenav.js | 2 +- .../universal-promo-terms.css | 9 ++ .../universal-promo-terms.js | 52 ++++++++++++ .../features/firefly/firefly-interactive.css | 32 +++++++- .../features/firefly/firefly-interactive.js | 66 ++++++++++----- test/blocks/sidenav/sidenav.test.html | 4 +- test/blocks/sidenav/sidenav.test.html.js | 7 +- .../universal-promo-terms/mocks/body.html | 3 + .../universal.promo.terms.test.js | 22 +++++ .../firefly/firefly-interactive.test.js | 48 +++++++++++ test/features/firefly/mocks/body.html | 82 +++++++++++++++++++ wtr | 0 12 files changed, 297 insertions(+), 30 deletions(-) create mode 100644 creativecloud/blocks/universal-promo-terms/universal-promo-terms.css create mode 100644 creativecloud/blocks/universal-promo-terms/universal-promo-terms.js create mode 100644 test/blocks/universal-promo-terms/mocks/body.html create mode 100644 test/blocks/universal-promo-terms/universal.promo.terms.test.js create mode 100644 wtr diff --git a/creativecloud/blocks/sidenav/sidenav.js b/creativecloud/blocks/sidenav/sidenav.js index f82b24d8b..43d07537a 100644 --- a/creativecloud/blocks/sidenav/sidenav.js +++ b/creativecloud/blocks/sidenav/sidenav.js @@ -5,7 +5,7 @@ import '../../deps/merch-sidenav.js'; const CATEGORY_ID_PREFIX = 'categories/'; const TYPE_ID_PREFIX = 'types/'; -const getIdLeaf = (id) => (id?.substring(id.lastIndexOf('/') + 1) || id); +const getIdLeaf = (id) => (id?.substring(id.lastIndexOf('/') + 1) || id).toLowerCase(); const getCategories = (items, isMultilevel, mapCategories) => { const configuration = { manageTabIndex: true }; diff --git a/creativecloud/blocks/universal-promo-terms/universal-promo-terms.css b/creativecloud/blocks/universal-promo-terms/universal-promo-terms.css new file mode 100644 index 000000000..d878b768f --- /dev/null +++ b/creativecloud/blocks/universal-promo-terms/universal-promo-terms.css @@ -0,0 +1,9 @@ +@media (min-width: 900px) { + body .feds-topnav-wrapper { + border: 0; + } +} + +body .global-footer { + background: none; +} diff --git a/creativecloud/blocks/universal-promo-terms/universal-promo-terms.js b/creativecloud/blocks/universal-promo-terms/universal-promo-terms.js new file mode 100644 index 000000000..11debfd40 --- /dev/null +++ b/creativecloud/blocks/universal-promo-terms/universal-promo-terms.js @@ -0,0 +1,52 @@ +const OFFER_ID_API_BASE = 'https://aos.adobe.io/offers/'; +const SELECTOR_ID_API_BASE = 'https://aos.adobe.io/offers:search.selector'; +const STAGE_OFFER_ID_API_BASE = 'https://aos-stage.adobe.io/offers/'; +const STAGE_SELECTOR_ID_API_BASE = 'https://aos-stage.adobe.io/offers:search.selector'; +const API_KEY = 'universalPromoTerm'; +const SERVICE_PROVIDERS = 'PROMO_TERMS'; + +function getEnv(env) { + if (env) return env; + if (window.location.origin.hostname === 'www.adobe.com') return 'production'; + return 'stage'; +} + +async function getTermsHTML(params, el, env, search) { + const locationSearch = search ?? window.location.search; + let promoTerms; + if (!params.get('offer_selector_ids')) { + let fetchURL = `${env === 'stage' ? STAGE_OFFER_ID_API_BASE : OFFER_ID_API_BASE}${params.get('offer_id')}${locationSearch}`; + fetchURL += params.get('api_key') ? '' : `&api_key=${API_KEY}`; + fetchURL += params.get('service_providers') ? '' : `&service_providers=${SERVICE_PROVIDERS}`; + + const res = await fetch(fetchURL); + if (!res.ok) return false; + const json = await res.json(); + promoTerms = json[0]?.promo_terms; + } else { + let fetchURL = `${env === 'stage' ? STAGE_SELECTOR_ID_API_BASE : SELECTOR_ID_API_BASE}${locationSearch}`; + fetchURL += params.get('api_key') ? '' : `&api_key=${API_KEY}`; + fetchURL += params.get('service_providers') ? '' : `&service_providers=${SERVICE_PROVIDERS}`; + + const res = await fetch(fetchURL); + if (!res.ok) return false; + const json = await res.json(); + promoTerms = json[0]?.offers[0]?.promo_terms; + } + + if (!promoTerms || !promoTerms.header || !promoTerms.text) { + return false; + } + return `
${el.innerHTML}

${promoTerms.header}

${promoTerms.text}

`; +} + +export default async function init(el, search) { + const params = new URLSearchParams(search ?? window.location.search); + const env = getEnv(params.get('env')); + const termsHTML = await getTermsHTML(params, el, env, search); + if (!termsHTML && env !== 'stage') { + window.location = '404.html'; + } else { + el.innerHTML = termsHTML; + } +} diff --git a/creativecloud/features/firefly/firefly-interactive.css b/creativecloud/features/firefly/firefly-interactive.css index 6c6a5aff2..3580afa72 100644 --- a/creativecloud/features/firefly/firefly-interactive.css +++ b/creativecloud/features/firefly/firefly-interactive.css @@ -79,11 +79,16 @@ @media (max-width: 600px) { .firefly-selectortray { - min-width: 110%; - margin-inline: -22px 16px; justify-content: center; margin-top: -7px; margin-bottom: 24px; + width: 80%; + margin-inline-start: 20px; + } + + .firefly-selectortray.three-options { + width: 110%; + margin-inline: -22px 16px; } .interactive-marquee.firefly .interactive-container { @@ -91,12 +96,17 @@ } .interactive-marquee.firefly .media { - top: 8px; + top: 24px; } .interactive-marquee.firefly .foreground { padding-top: 0; } + + .interactive-marquee.firefly.ff-text-effects .interactive-container, + .interactive-marquee.firefly.ff-text-to-image .interactive-container { + height: 346px; + } } @media (min-width: 600px) and (max-width: 1199px) { @@ -113,6 +123,11 @@ .firefly-selectortray { top: 0; + width: 50%; + margin-inline: 126px; + } + + .firefly-selectortray.three-options { margin-inline: 42px; width: 80%; } @@ -124,15 +139,24 @@ .interactive-marquee.firefly .foreground { padding-top: 0; } + + .interactive-marquee.firefly.ff-text-effects .interactive-container, + .interactive-marquee.firefly.ff-text-to-image .interactive-container { + height: 662px; + } } @media screen and (min-width: 1200px) { .firefly-selectortray { - top: 144px; + top: 187px; position: absolute; right: -122px; } + .firefly-selectortray.three-options { + top: 144px; + } + [dir="rtl"] .firefly-selectortray { left: -122px; right: 446px; diff --git a/creativecloud/features/firefly/firefly-interactive.js b/creativecloud/features/firefly/firefly-interactive.js index 35d7ab200..809de22cb 100644 --- a/creativecloud/features/firefly/firefly-interactive.js +++ b/creativecloud/features/firefly/firefly-interactive.js @@ -1,21 +1,26 @@ import { getLibs } from '../../scripts/utils.js'; +const { default: defineDeviceByScreenSize } = await import('../../scripts/decorate.js'); + function focusOnInput(media, createTag) { const input = media.querySelector('.prompt-text'); if (input) { - input.focus(); + const device = defineDeviceByScreenSize(); + const blinkingCursor = createTag('div', { class: 'blinking-cursor' }); + if (input.classList.contains('light')) blinkingCursor.classList.add('blink-light'); + if (device === 'MOBILE' || device === 'TABLET') { + input.insertAdjacentElement('beforebegin', blinkingCursor); + } else input.focus(); input.addEventListener('focusout', () => { - if (document.querySelector('.locale-modal-v2')) { - const blinkingCursor = createTag('div', { class: 'blinking-cursor' }); + if (document.querySelector('.locale-modal-v2') && device === 'DESKTOP') { input.insertAdjacentElement('beforebegin', blinkingCursor); - if (input.classList.contains('light')) blinkingCursor.classList.add('blink-light'); } }, { once: true }); input.addEventListener('click', () => { document.querySelector('.blinking-cursor')?.remove(); }); } } -function eventOnGenerate(generateButton, media) { +function eventOnGenerate(generateButton, media, fireflyfeature = '') { const btnConfigs = { TextToImage: ['SubmitTextToImage', 'SubmitTextToImageUserContent', 'goToFirefly'], TextEffects: ['SubmitTextEffects', 'SubmitTextEffectsUserContent', 'goToFireflyEffects'], @@ -24,8 +29,11 @@ function eventOnGenerate(generateButton, media) { const userprompt = media.querySelector('.prompt-text')?.value; const placeholderprompt = media.querySelector('.prompt-text')?.getAttribute('placeholder'); const prompt = userprompt || placeholderprompt; - const selected = media.querySelector('.selected'); - const className = selected.getAttribute('class').split(' ')[1].trim(); + let className = ''; + if (fireflyfeature === '') { + const selected = media.querySelector('.selected'); + className = selected.getAttribute('class').split(' ')[1].trim(); + } else className = fireflyfeature; if (Object.keys(btnConfigs).includes(className)) { const btnConfig = btnConfigs[className]; const dall = userprompt === '' ? btnConfig[0] : btnConfig[1]; @@ -86,6 +94,16 @@ async function eventOnSelectorOption(option, prompt, media, mediaP, createPrompt } } +async function singleFireflyFeature(promptDet, mode, createPromptField, media, feature, createTag) { + const firstOptionDetail = promptDet.innerText.split('|'); + const fireflyPrompt = await createPromptField(`${firstOptionDetail[0]}`, `${firstOptionDetail[1]}`, mode); + fireflyPrompt.classList.add('firefly-prompt'); + media.appendChild(fireflyPrompt); + const generateButton = media.querySelector('#promptbutton'); + eventOnGenerate(generateButton, media, feature); + focusOnInput(media, createTag); +} + export default async function setInteractiveFirefly(el) { const enticementMode = el.classList.contains('light') ? 'light' : 'dark'; const interactiveElemMode = el.classList.contains('light') ? 'dark' : 'light'; @@ -128,7 +146,6 @@ export default async function setInteractiveFirefly(el) { }; selections.push(option); }); - [...allP].forEach((s) => { if (!s.querySelector('picture') && !s.querySelector('video')) s.remove(); }); const mediaP = media.querySelectorAll('p:not(:empty)'); [...mediaP].forEach((image) => { image.classList.add('hide'); }); @@ -139,24 +156,31 @@ export default async function setInteractiveFirefly(el) { const enticementIcon = allAnchorTag[0].href; const enticementDiv = await createEnticement(`${enticementText}|${enticementIcon}`, enticementMode); media.appendChild(enticementDiv, media.firstChild); - + const { createTag } = await import(`${getLibs()}/utils/utils.js`); + if (el.classList.contains('ff-text-effects')) { + mediaP[0].classList.remove('hide'); + singleFireflyFeature(allP[2], interactiveElemMode, createPromptField, media, 'TextEffects', createTag); + return; + } + if (el.classList.contains('ff-text-to-image')) { + mediaP[0].classList.remove('hide'); + singleFireflyFeature(allP[2], interactiveElemMode, createPromptField, media, 'TextToImage', createTag); + return; + } const fireflyOptions = await createSelectorTray(selections, interactiveElemMode); fireflyOptions.classList.add('firefly-selectortray'); + if (selections.length === 3) fireflyOptions.classList.add('three-options'); media.append(fireflyOptions); - const ttiOption = media.querySelector('.TextToImage'); const genFillOption = media.querySelector('.GenerativeFill'); const teOption = media.querySelector('.TextEffects'); const firstOption = media.querySelector('.selector-tray > button'); - hideRemoveElements(firstOption, media, mediaP); - const { createTag } = await import(`${getLibs()}/utils/utils.js`); - - const genfillPrompt = createGenFillPrompt(genfDetail.promptpos, createTag); // Create prompt field for first option on page load const firstOptionDetail = allP[3].innerText.split('|'); - const fireflyPrompt = await createPromptField(`${firstOptionDetail[0]}`, `${firstOptionDetail[1]}`, interactiveElemMode); + const mode = firstOption.classList.contains('GenerativeFill') ? 'genfill' : interactiveElemMode; + const fireflyPrompt = await createPromptField(`${firstOptionDetail[0]}`, `${firstOptionDetail[1]}`, mode); if (firstOption.classList.contains('TextToImage') || firstOption.classList.contains('TextEffects')) { fireflyPrompt.classList.add('firefly-prompt'); media.appendChild(fireflyPrompt); @@ -164,6 +188,7 @@ export default async function setInteractiveFirefly(el) { eventOnGenerate(generateButton, media); } else if (firstOption.classList.contains('GenerativeFill')) { fireflyPrompt.classList.add('genfill-promptbar'); + const genfillPrompt = createGenFillPrompt(genfDetail.promptpos, createTag); media.append(genfillPrompt, fireflyPrompt); const genFillButton = media.querySelector('#genfill'); genFillButton.addEventListener('click', async () => { @@ -171,20 +196,17 @@ export default async function setInteractiveFirefly(el) { signIn('', 'goToFireflyGenFill'); }); } - focusOnInput(media, createTag); /* Handle action on click of each firefly option button */ - - ttiOption.addEventListener('click', () => { + ttiOption?.addEventListener('click', () => { eventOnSelectorOption(ttiOption, ttiDetail, media, mediaP, createPromptField, createTag); }); - - genFillOption.addEventListener('click', () => { + genFillOption?.addEventListener('click', () => { eventOnSelectorOption(genFillOption, genfDetail, media, mediaP, createPromptField); + const genfillPrompt = createGenFillPrompt(genfDetail.promptpos, createTag); media.appendChild(genfillPrompt); }); - - teOption.addEventListener('click', () => { + teOption?.addEventListener('click', () => { eventOnSelectorOption(teOption, teDetail, media, mediaP, createPromptField, createTag); }); } diff --git a/test/blocks/sidenav/sidenav.test.html b/test/blocks/sidenav/sidenav.test.html index 19e1b3b7f..d50e7e1a6 100644 --- a/test/blocks/sidenav/sidenav.test.html +++ b/test/blocks/sidenav/sidenav.test.html @@ -35,8 +35,8 @@

REFINE YOUR RESULTS

  • Social-media
  • Pdf
  • Esignatures
  • -
  • Coldfusion
  • -
  • Elearning
  • +
  • ColdFusion
  • +
  • ELearning
  • Print-imaging
  • Technical-communication
  • Insight-audiences
  • diff --git a/test/blocks/sidenav/sidenav.test.html.js b/test/blocks/sidenav/sidenav.test.html.js index c4de81ff5..51131de2c 100644 --- a/test/blocks/sidenav/sidenav.test.html.js +++ b/test/blocks/sidenav/sidenav.test.html.js @@ -36,6 +36,9 @@ runTests(async () => { expect(newRoot.title).to.equal('REFINE YOUR RESULTS'); const items = newRoot.querySelectorAll('sp-sidenav-item'); expect(items.length).to.equal(expectedItemCount); + const found = Array.from(items).find((item) => item.getAttribute('label') === 'ColdFusion'); + expect(found).to.not.be.undefined; + expect(found.getAttribute('value')).to.equal('coldfusion'); const search = newRoot.querySelector('sp-search'); expect(search.getAttribute('placeholder')).to.equal('Search all your products'); expect(newRoot.querySelectorAll('sp-checkbox').length).to.equal(3); @@ -49,7 +52,9 @@ runTests(async () => { }); it('does create nice reordered categories sidenav', async () => { - await testCategorySidenav('.reordered-categories', 17, 11); + // 16 items from html requirements, 2 parents from taxonomy, + // and special-offer special case is 19 + await testCategorySidenav('.reordered-categories', 19, 13); }); it('does create nice plans sidenav', async () => { diff --git a/test/blocks/universal-promo-terms/mocks/body.html b/test/blocks/universal-promo-terms/mocks/body.html new file mode 100644 index 000000000..22fa7c98e --- /dev/null +++ b/test/blocks/universal-promo-terms/mocks/body.html @@ -0,0 +1,3 @@ +
    +
    +
    diff --git a/test/blocks/universal-promo-terms/universal.promo.terms.test.js b/test/blocks/universal-promo-terms/universal.promo.terms.test.js new file mode 100644 index 000000000..e7b4939ae --- /dev/null +++ b/test/blocks/universal-promo-terms/universal.promo.terms.test.js @@ -0,0 +1,22 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; +import sinon from 'sinon'; + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +const { default: init } = await import('../../../creativecloud/blocks/universal-promo-terms/universal-promo-terms.js'); + +describe('universal-promo-terms', () => { + const block = document.body.querySelector('.universal-promo-terms'); + beforeEach(() => { + sinon.spy(console, 'log'); + }); + + afterEach(() => { + console.log.restore(); + }); + + it('Get API from query parameters', async () => { + await init(block, '?offer_id=1B365A793986BBEEE26F3E372BDAAB09&locale=de_DE&promotion_code=fixed_dis_20&country=DE&env=stage'); + expect(document.querySelector('.universal-promo-terms').textContent).to.not.equal('false'); + }); +}); diff --git a/test/features/firefly/firefly-interactive.test.js b/test/features/firefly/firefly-interactive.test.js index 9b7122e07..2334ec68c 100644 --- a/test/features/firefly/firefly-interactive.test.js +++ b/test/features/firefly/firefly-interactive.test.js @@ -64,3 +64,51 @@ describe('firefly-marquee', () => { expect(texteffectPrompt).to.exist; }); }); + +describe('firefly-text-effect-marquee', () => { + before(async () => { + document.body.innerHTML = await readFile({ path: './mocks/body.html' }); + await init(document.querySelector('.ff-text-effects')); + }); + + it('Prompt should exist', async () => { + const promptbar = await waitForElement('.promptbar'); + expect(promptbar).to.exist; + }); + + it('Prompt should be placed at proper place in interactive container', async () => { + const fireflypromptbar = await waitForElement('.firefly-prompt'); + expect(fireflypromptbar).to.exist; + }); + + it('Enticement should exist', async () => { + const enticementText = await waitForElement('.enticement-text'); + const enticementArrow = await waitForElement('.enticement-arrow'); + expect(enticementText).to.exist; + expect(enticementArrow).to.exist; + }); +}); + +describe('firefly-text-to-image-marquee', () => { + before(async () => { + document.body.innerHTML = await readFile({ path: './mocks/body.html' }); + await init(document.querySelector('.ff-text-to-image')); + }); + + it('Prompt should exist', async () => { + const promptbar = await waitForElement('.promptbar'); + expect(promptbar).to.exist; + }); + + it('Prompt should be placed at proper place in interactive container', async () => { + const fireflypromptbar = await waitForElement('.firefly-prompt'); + expect(fireflypromptbar).to.exist; + }); + + it('Enticement should exist', async () => { + const enticementText = await waitForElement('.enticement-text'); + const enticementArrow = await waitForElement('.enticement-arrow'); + expect(enticementText).to.exist; + expect(enticementArrow).to.exist; + }); +}); diff --git a/test/features/firefly/mocks/body.html b/test/features/firefly/mocks/body.html index 9a70e2233..9865a5716 100644 --- a/test/features/firefly/mocks/body.html +++ b/test/features/firefly/mocks/body.html @@ -44,3 +44,85 @@

    Your imagination’s new best friend. +
    +
    +
    linear-gradient(90deg, rgba(238,174,202,1) 0%, rgba(148,187,233,1) 100% +
    +
    +
    +
    +

    + + Adobe Firefly +

    +

    Your imagination’s new best friend.

    +

    Use simple prompts and generative AI to create anything you can imagine with the new Adobe Firefly web app. + From photorealistic portraits and fantasy creatures to text effects and fresh color palettes, the + possibilities + are pure magic. Now available for commercial use.

    +

    Get Firefly Free

    +
    +
    +

    Try it

    +

    -----------------------------------------------------------

    +

    Tiger Fur|Generate

    +

    + + + + + + +

    +
    +
    +
    +
    +
    +
    linear-gradient(90deg, rgba(238,174,202,1) 0%, rgba(148,187,233,1) 100% +
    +
    +
    +
    +

    + + Adobe Firefly +

    +

    Your imagination’s new best friend.

    +

    Use simple prompts and generative AI to create anything you can imagine with the new Adobe Firefly web app. + From photorealistic portraits and fantasy creatures to text effects and fresh color palettes, the + possibilities + are pure magic. Now available for commercial use.

    +

    Get Firefly Free

    +
    +
    +

    Try it

    +

    -----------------------------------------------------------

    +

    Tiger Fur|Generate

    +

    + + + + + + +

    +
    +
    +
    diff --git a/wtr b/wtr new file mode 100644 index 000000000..e69de29bb