From 0996c7a1cf0624296bc254a1db47ed667b915d80 Mon Sep 17 00:00:00 2001 From: Nicolas Peltier Date: Thu, 28 Mar 2024 10:16:35 +0100 Subject: [PATCH 01/10] @releng better hlxignore - remove root json, - remove code coverage, - generic rule for sidekick like nested json --- .hlxignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.hlxignore b/.hlxignore index 7cbf3cd69..f14c1eb89 100644 --- a/.hlxignore +++ b/.hlxignore @@ -1,5 +1,8 @@ +*.json +!*/**/*.json .* *.md LICENSE test/* -!tools/sidekick/config.json +web-test-runner.config.mjs +codecov.yaml From 54895b32f3c1ddffa8df559688a2a8c0fdad0391 Mon Sep 17 00:00:00 2001 From: Drashti Modasara Date: Fri, 5 Apr 2024 13:15:22 +0530 Subject: [PATCH 02/10] Fixed RTL positioning of enticement on genfill int-marquee (#257) Fixed the enticement position in genfill interactive marquees for RTL locale in page and fragment. Resolves: MWPW-145840 Test URLs: Before: https://main--cc--adobecom.hlx.live/drafts/drashti/bugs/genfill/document After: https://genfill-stage--cc--adobecom.hlx.live/drafts/drashti/bugs/genfill/document --- .../features/genfill/genfill-interactive.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/creativecloud/features/genfill/genfill-interactive.css b/creativecloud/features/genfill/genfill-interactive.css index 952e598b0..dbe0d66bd 100644 --- a/creativecloud/features/genfill/genfill-interactive.css +++ b/creativecloud/features/genfill/genfill-interactive.css @@ -70,6 +70,13 @@ [dir="rtl"] .enticement-text { position: absolute; left: var(--spacing-xxs); + right: var(--spacing-xxs); + margin: 40px -20px 16px 0; + } + + [dir="rtl"] .enticement-arrow { + right: -54px; + transform: scaleX(-1); } } @@ -91,5 +98,12 @@ [dir="rtl"] .enticement-text { position: absolute; left: var(--spacing-xxs); + right: var(--spacing-xxs); + margin: 40px -20px 16px 0; + } + + [dir="rtl"] .enticement-arrow { + right: -54px; + transform: scaleX(-1); } } From 1576e46ce66fe3dd5563f46127664876009a6c13 Mon Sep 17 00:00:00 2001 From: Aishwarya Mathuria Date: Mon, 8 Apr 2024 18:58:16 +0530 Subject: [PATCH 03/10] MWPW-142971 | Interactive web components over Marquee and Aside (#251) Interactive web components over Marquee and Aside Requirement, Architecture and Demo Wiki: https://wiki.corp.adobe.com/pages/viewpage.action?pageId=3127606992 Resolves: [MWPW-142971](https://jira.corp.adobe.com/browse/MWPW-142971) Ps Interactive Marquee + Aside [MWPW-142980](https://jira.corp.adobe.com/browse/MWPW-142980) GenFill Marquee + Aside Interactive PS pages [MWPW-142976](https://jira.corp.adobe.com/browse/MWPW-142976) Slider (Hue Sat) Interactive Marquee + Aside Test URLs: Before: https://main--cc--adobecom.hlx.live/drafts/mathuria/interactiveelems/interactive-workflow4?martech=off After: https://interactivity--cc--adobecom.hlx.live/drafts/mathuria/interactiveelems/interactive-workflow4?martech=off --- .../interactive-metadata.css | 439 ++++++++++++++++++ .../interactive-metadata.js | 301 ++++++++++++ .../interactive-components/crop/crop.css | 75 +++ .../interactive-components/crop/crop.js | 23 + .../generate/generate.css | 162 +++++++ .../generate/generate.js | 32 ++ .../selector-tray/selector-tray.css | 125 +++++ .../selector-tray/selector-tray.js | 89 ++++ .../slider-tray/slider-tray.css | 358 ++++++++++++++ .../slider-tray/slider-tray.js | 263 +++++++++++ .../start-over/start-over.css | 20 + .../start-over/start-over.js | 44 ++ creativecloud/scripts/utils.js | 2 +- creativecloud/styles/styles.css | 11 +- .../interactive-metadata.test.js | 72 +++ .../mocks/assets/icon_.svg | 17 + .../mocks/assets/media_.png | Bin 0 -> 1346 bytes .../mocks/interactive-metadata.html | 212 +++++++++ .../interactive-components/crop/crop.test.js | 26 ++ .../crop/mocks/body.html | 50 ++ .../generate/generate.test.js | 25 + .../generate/mocks/body.html | 37 ++ .../selector-tray/mocks/body.html | 213 +++++++++ .../selector-tray/selector-tray.test.js | 41 ++ .../slider-tray/mocks/body.html | 73 +++ .../slider-tray/slider-tray.test.js | 71 +++ .../start-over/mocks/body.html | 36 ++ .../start-over/start-over.test.js | 34 ++ 28 files changed, 2840 insertions(+), 11 deletions(-) create mode 100644 creativecloud/blocks/interactive-metadata/interactive-metadata.css create mode 100644 creativecloud/blocks/interactive-metadata/interactive-metadata.js create mode 100644 creativecloud/features/interactive-components/crop/crop.css create mode 100644 creativecloud/features/interactive-components/crop/crop.js create mode 100644 creativecloud/features/interactive-components/generate/generate.css create mode 100644 creativecloud/features/interactive-components/generate/generate.js create mode 100644 creativecloud/features/interactive-components/selector-tray/selector-tray.css create mode 100644 creativecloud/features/interactive-components/selector-tray/selector-tray.js create mode 100644 creativecloud/features/interactive-components/slider-tray/slider-tray.css create mode 100644 creativecloud/features/interactive-components/slider-tray/slider-tray.js create mode 100644 creativecloud/features/interactive-components/start-over/start-over.css create mode 100644 creativecloud/features/interactive-components/start-over/start-over.js create mode 100644 test/blocks/interactive-metadata/interactive-metadata.test.js create mode 100644 test/blocks/interactive-metadata/mocks/assets/icon_.svg create mode 100644 test/blocks/interactive-metadata/mocks/assets/media_.png create mode 100644 test/blocks/interactive-metadata/mocks/interactive-metadata.html create mode 100644 test/features/interactive-components/crop/crop.test.js create mode 100644 test/features/interactive-components/crop/mocks/body.html create mode 100644 test/features/interactive-components/generate/generate.test.js create mode 100644 test/features/interactive-components/generate/mocks/body.html create mode 100644 test/features/interactive-components/selector-tray/mocks/body.html create mode 100644 test/features/interactive-components/selector-tray/selector-tray.test.js create mode 100644 test/features/interactive-components/slider-tray/mocks/body.html create mode 100644 test/features/interactive-components/slider-tray/slider-tray.test.js create mode 100644 test/features/interactive-components/start-over/mocks/body.html create mode 100644 test/features/interactive-components/start-over/start-over.test.js diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.css b/creativecloud/blocks/interactive-metadata/interactive-metadata.css new file mode 100644 index 000000000..f01cb4a94 --- /dev/null +++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.css @@ -0,0 +1,439 @@ + +:root { + --prompt-input-fill: #262626; + --prompt-btn-fill: #323232; + --prompt-text-color: #D1D1D1; + --prompt-text-border: #545454; + --prompt-btn-text: #FFF; + --prompt-highlight-color: #1473E6; + --layer-background-fill: #272928; + --prompt-btn-fill-light: #FFFF; + --prompt-text-color-light: #2C2C2C; + --prompt-input-fill-light: #D5D5D5; +} + +.interactive-enabled .foreground .image > p:first-child { + display: none; + margin: 0; +} + +.interactive-enabled.heading-top h1, +.interactive-enabled.heading-top h2, +.interactive-enabled.heading-top h3, +.interactive-enabled.heading-top h4, +.interactive-enabled.heading-top h5, +.interactive-enabled.heading-top h6, +.interactive-enabled .foreground .image .mobile-top-title { + display: none; +} + +.interactive-enabled .foreground.container .image { + flex-direction: column; +} + +.aside.interactive-enabled .foreground.container { + margin: 40px auto 32px; + flex-direction: column; + gap: var(--spacing-m); + padding: 0; +} + +.aside.interactive-enabled .foreground.container > div.image, +.aside.interactive-enabled .foreground.container > div.text { + margin: 0; + padding: 0; +} + +.interactive-metadata, +.interactive-enabled .layer { + display: none; +} + +.interactive-enabled .foreground.container .interactive-holder { + margin: auto; + aspect-ratio: 1; +} + +.interactive-enabled .foreground .image .enticement-container { + display: block; + height: 28px; + font-weight: bold; + font-size: 28px; + line-height: 28px; + margin: 0 0 16px; +} + +.interactive-enabled.mobile-hide-enticement .foreground .image .enticement-container { + display: none; +} + +.interactive-enabled .foreground .image .enticement-message { + display: block; + max-width: 30ch; + font-size: 28px; + line-height: 35px; +} + +.interactive-enabled .foreground .image .enticement-svg { + display: block; + position: relative; + width: 49px; + height: 73px; + top: -16px; + right: 30px; + left: auto; + z-index: 2; + margin: 0; +} + +[dir="rtl"] .interactive-enabled .foreground .image .enticement-svg { + left: 30px; + right: auto; + transform: scaleX(-1); +} + +.interactive-enabled .foreground .image > p > img[src*='svg'] { + display: block; +} + +.interactive-enabled .foreground.container .interactive-holder > picture img, +.interactive-enabled .foreground.container .interactive-holder > picture, +.interactive-enabled .foreground.container .interactive-holder > video { + object-fit: cover; + min-height: unset; + border-radius: 8px 8px 0 0; + height: 0; + z-index: 0; + opacity: 0; +} + +.interactive-enabled .foreground.container .interactive-holder.show-image > picture img, +.interactive-enabled .foreground.container .interactive-holder.show-image > picture, +.interactive-enabled .foreground.container .interactive-holder.show-video > video { + width: 100%; + aspect-ratio: 1; + height: auto; + opacity: 1; + z-index: 1; +} + +.interactive-enabled .layer.show-layer { + position: relative; + top: auto; + bottom: 0; + left: 0; + width: 100%; + height: auto; + min-height: 60px; + background: var(--layer-background-fill); + display: flex; + align-items: center; + justify-content: center; + border-radius: 0 0 8px 8px; + z-index: 2; +} + +.interactive-enabled .layer .gray-button { + position: absolute; + box-sizing: border-box; + outline: none; + border-radius: 8px; + border: 3px solid transparent; + cursor: pointer; + display: flex; + gap: 8px; + padding: 16px; + text-decoration: none; + color: var(--prompt-btn-text); + background: var(--prompt-btn-fill); + font-weight: bold; + align-items: center; + -webkit-tap-highlight-color: transparent; + -webkit-user-select: none; + user-select: none; + white-space: nowrap; +} + +.interactive-enabled.light .layer .gray-button { + color: var(--prompt-text-color-light); + background: var(--prompt-btn-fill-light); +} + +.interactive-enabled .interactive-holder.dark .layer .gray-button { + color: var(--prompt-btn-text); + background: var(--prompt-btn-fill); +} + +.interactive-enabled .interactive-holder.light .layer .gray-button { + color: var(--prompt-text-color-light); + background: var(--prompt-btn-fill-light); +} + +.interactive-enabled .layer .gray-button.animated { + animation: outline-fill 2700ms 500ms forwards 7; +} + +.interactive-enabled .layer .gray-button.animated::before { + content: ''; + position: absolute; + left: 0; + right: 0; + margin: 0 auto; + width: 40px; + height: 40px; + box-sizing: border-box; + border: 8px solid var(--prompt-highlight-color); + border-radius: 50%; + opacity: 0; + animation: circle 2700ms ease-in-out 500ms backwards 7; +} + +.aside.interactive-enabled .foreground.container .interactive-holder { + flex: 0 0 auto !important; +} + +@keyframes circle { + 0% { opacity: 0; } + + 18% { + transform: scale(100%); + opacity: 0.5; + } + + 29% { + transform: scale(100%); + opacity: 0.5; + } + + 44% { + transform: scale(50%); + opacity: 1; + } + + 50% { opacity: 0; } +} + +@keyframes outline-fill { + 0% { border-color: transparent ; } + + 18% { border-color: var(--prompt-highlight-color); } + + 24% { border-color: var(--prompt-highlight-color); } + + 59% { + border-color: var(--prompt-highlight-color); + background: var(--prompt-highlight-color); + } + + 90% { border-color: var(--prompt-highlight-color); } +} + +.interactive-enabled .foreground.container .interactive-holder .layer .gray-button img, +.interactive-enabled .foreground.container .interactive-holder .layer .gray-button picture { + width: 24px; + height: auto; + min-height: 24px; + max-width: 24px; + border-radius: 0; +} + +.interactive-enabled .layer .gray-button:focus-visible, +.interactive-enabled .layer .gray-button:hover { + border: 3px solid var(--prompt-highlight-color); + background: var(--prompt-btn-fill); + animation-play-state: paused; +} + +.interactive-enabled.light .layer .gray-button:focus-visible, +.interactive-enabled.light .layer .gray-button:hover { + background: var(--prompt-btn-fill-light); +} + +.interactive-enabled .interactive-holder.dark .layer .gray-button:focus-visible, +.interactive-enabled .interactive-holder.dark .layer .gray-button:hover { + background: var(--prompt-btn-fill); +} + +.interactive-enabled .interactive-holder.light .layer .gray-button:focus-visible, +.interactive-enabled .interactive-holder.light .layer .gray-button:hover { + background: var(--prompt-btn-fill-light); +} + +.interactive-enabled .interactive-link-analytics-text { + display: none; +} + +@media screen and (max-width: 600px) { + .aside.interactive-enabled .foreground.container .image { + display: block; + width: 100%; + } + + .interactive-enabled.heading-top .foreground .image .mobile-top-title { + display: block; + margin-bottom: 24px; + font-size: 36px; + line-height: 45px; + font-weight: bold; + } +} + +@media screen and (min-width: 600px) { + .interactive-enabled.heading-top h1, + .interactive-enabled.heading-top h2, + .interactive-enabled.heading-top h3, + .interactive-enabled.heading-top h4, + .interactive-enabled.heading-top h5, + .interactive-enabled.heading-top h6 { + display: block; + } + + .interactive-enabled .foreground.container .image > p:first-child { + display: none; + } + + .interactive-enabled .foreground .image { + position: relative; + border-radius: 16px; + margin-inline-start: 0; + background: none; + width: calc(var(--grid-container-width) - 20px); + max-width: 570px; + margin: 36px auto 42px; + } + + .interactive-enabled .interactive-holder { + position: relative; + max-width: 570px; + max-height: 570px; + width: 100%; + } + + .interactive-enabled .foreground.container .image .interactive-holder > picture img, + .interactive-enabled .foreground.container .image .interactive-holder > picture, + .interactive-enabled .foreground.container .image .interactive-holder > video { + border-radius: 16px; + } + + .interactive-enabled .layer.show-layer { + display: flex; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: none; + } + + .interactive-enabled .foreground.container .image .enticement-container { + display: block; + position: relative; + width: var(--grid-container-width); + max-width: 570px; + right: 30px; + margin: 0 0 24px; + } + + [dir="rtl"] .interactive-enabled .foreground.container .image .enticement-container { + right: auto; + left: 30px; + } + + [dir="rtl"] .interactive-enabled .foreground.container .image .enticement-container .enticement-svg { + right: auto; + left: 30px; + transform: scaleX(-1); + } + + .interactive-enabled .foreground.container .interactive-holder .layer .gray-button picture, + .interactive-enabled .foreground.container .interactive-holder .layer .gray-button img { + width: 34px; + height: auto; + min-height: 34px; + max-width: 34px; + border-radius: 0; + } + + .interactive-enabled .layer .gray-button { + font-size: var(--type-body-xl-size); + line-height: var(--type-body-xl-lh); + box-shadow: 0 10px 10px rgba(0, 0, 0, 32%); + } + + .interactive-enabled .layer .gray-button.animated::before { + width: 56px; + height: 56px; + } +} + +@media screen and (max-width: 1200px) { + .aside.interactive-enabled .foreground.container .image { + order: 1; + } + + .aside.interactive-enabled .foreground.container .text { + order: 2; + } +} + +@media screen and (min-width: 1200px) { + .aside.interactive-enabled .foreground.container > div { + min-width: unset; + } + + .aside.interactive-enabled .foreground.container div.image, + .marquee.interactive-enabled .foreground.container div.image { + max-width: 570px; + margin-inline-start: 60px; + } + + .interactive-enabled .foreground.container .interactive-holder { + margin: 0; + } + + [dir="rtl"] .aside.interactive-enabled .foreground.container div.image, + .aside.interactive-enabled.row-reversed .foreground.container div.image { + margin: 36px 60px 0 0; + height: 664px; + } + + .aside.interactive-enabled .foreground.container div.image, + [dir="rtl"] .aside.interactive-enabled.row-reversed .foreground.container div.image { + height: 664px; + margin: 36px 0 0 60px; + } + + .interactive-enabled .foreground .image .enticement-svg { + display: flex; + flex-direction: row-reverse; + justify-content: start; + min-height: unset; + height: 73px; + width: 49px; + } + + .marquee.interactive-enabled .foreground.container, + .aside.interactive-enabled .foreground.container { + padding-top: 0; + padding-bottom: 0; + } + + .aside.interactive-enabled .foreground.container { + flex-direction: row; + align-items: center; + margin: 0; + min-height: 700px; + } + + .marquee.interactive-enabled .foreground.container, + .aside.interactive-enabled.row-reversed .foreground.container { + order: unset; + flex-direction: row; + } + + .aside.interactive-enabled.row-reversed .foreground.container { + flex-direction: row-reverse; + } +} + + diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.js b/creativecloud/blocks/interactive-metadata/interactive-metadata.js new file mode 100644 index 000000000..8fd7e47b4 --- /dev/null +++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.js @@ -0,0 +1,301 @@ +import { getLibs, createTag, loadStyle } from '../../scripts/utils.js'; +import defineDeviceByScreenSize from '../../scripts/decorate.js'; + +export function getImgSrc(pic) { + const viewport = defineDeviceByScreenSize() === 'MOBILE' ? 'mobile' : 'desktop'; + let source = ''; + if (viewport === 'mobile') source = pic.querySelector('source[type="image/webp"]:not([media])'); + else source = pic.querySelector('source[type="image/webp"][media]'); + return source.srcset; +} + +function getNextStepIndex(stepInfo) { + return (stepInfo.stepIndex + 1) % stepInfo.stepList.length; +} + +function getPrevStepIndex(stepInfo) { + return stepInfo.stepIndex - 1 >= 0 + ? stepInfo.stepIndex - 1 + : stepInfo.stepList.length - 1; +} + +function animationCallback(btn) { + btn.classList.add('animated'); + ['mouseover', 'focus'].forEach((event) => { + btn.addEventListener(event, () => { btn.classList.remove('animated'); }); + }); +} + +async function addLayerAnimation(asset) { + const ioEl = asset.querySelector('.gray-button'); + if (!ioEl) return; + const miloLibs = getLibs('/libs'); + const { createIntersectionObserver } = await import(`${miloLibs}/utils/utils.js`); + createIntersectionObserver({ + el: ioEl, + callback: animationCallback, + options: { threshold: 0.7 }, + }); +} + +async function loadJSandCSS(stepName) { + const stepJS = `${window.location.origin}/creativecloud/features/interactive-components/${stepName}/${stepName}.js`; + const stepCSS = `${window.location.origin}/creativecloud/features/interactive-components/${stepName}/${stepName}.css`; + loadStyle(stepCSS); + const { default: initFunc } = await import(stepJS); + return initFunc; +} + +function loadImg(img) { + return new Promise((res) => { + img.loading = 'eager'; + img.fetchpriority = 'high'; + if (img.complete) res(); + else { + img.onload = () => res(); + img.onerror = () => res(); + } + }); +} + +function preloadAsset(nextStepIndex, stepInfo) { + const das = stepInfo.stepConfigs[nextStepIndex] + .querySelectorAll(':scope > div > p > picture img[src*="media_"], :scope > div > p > a[href*=".mp4"]'); + if (!das.length) return; + const { displayPath } = stepInfo; + const daIdx = (displayPath < das.length) ? displayPath : 0; + const da = das[daIdx]; + if (da.nodeName === 'A') { + const { pathname } = new URL(da.href); + const video = createTag('video', { src: pathname }); + video.load(); + } else if (da.nodeName === 'IMG') { + const src = getImgSrc(da.closest('picture')); + fetch(src); + } +} + +async function loadAllImgs(imgs) { + const promiseLst = []; + [...imgs].forEach((img) => { + promiseLst.push(loadImg(img)); + }); + await Promise.allSettled(promiseLst); +} + +async function createDisplayImg(target, replaceEl, src, alt) { + const newImg = createTag('img', { src, alt }); + const img = replaceEl.querySelector('img'); + await loadImg(newImg); + img.src = src; + img.alt = alt; + target.classList.add('show-image'); + target.classList.remove('show-video'); +} + +async function createDisplayVideo(target, video, src) { + const { pathname, hash } = new URL(src); + const attrs = { src: pathname, playsinline: '', autoplay: '', muted: '', type: 'video/mp4' }; + if (hash?.includes('autoplay1')) video.removeAttribute('loop'); + else attrs.loop = ''; + Object.keys(attrs).forEach((attr) => video.setAttribute(attr, attrs[attr])); + try { + video.load(); + await video.play(); + } catch (err) { return; } + target.classList.add('show-video'); + target.classList.remove('show-image'); +} + +export async function handleImageTransition(stepInfo, transitionCfg = {}) { + const config = stepInfo.stepConfigs[stepInfo.stepIndex].querySelector('div'); + const trgtPic = stepInfo.target.querySelector(':scope > picture'); + const trgtVideo = stepInfo.target.querySelector(':scope > video'); + if (transitionCfg.useCfg) { + if (transitionCfg.src) { + await createDisplayImg(stepInfo.target, trgtPic, transitionCfg.src, transitionCfg.alt); + } else { + await createDisplayVideo(stepInfo.target, trgtVideo, transitionCfg.vsrc); + } + return; + } + const displayPics = config.querySelectorAll(':scope > p > picture img[src*="media_"]'); + const displayVideos = config.querySelectorAll(':scope > p > a[href*=".mp4"]'); + const { displayPath } = stepInfo; + if (displayPics.length) { + const imgIdx = (displayPath < displayPics.length) ? displayPath : 0; + const picSrc = getImgSrc(displayPics[imgIdx].closest('picture')); + await createDisplayImg(stepInfo.target, trgtPic, picSrc, displayPics[imgIdx].alt); + } else if (displayVideos.length) { + const vidIdx = (displayPath < displayVideos.length) ? displayPath : 0; + await createDisplayVideo(stepInfo.target, trgtVideo, displayVideos[vidIdx].href); + } +} + +async function handleNextStep(stepInfo) { + const nextStepIndex = getNextStepIndex(stepInfo); + stepInfo.stepInit = await loadJSandCSS(stepInfo.stepList[nextStepIndex]); + await loadAllImgs(stepInfo.stepConfigs[nextStepIndex].querySelectorAll('img[src*="svg"]')); + preloadAsset(nextStepIndex, stepInfo); +} + +async function handleLayerDisplay(stepInfo) { + const clsLayer = stepInfo.target.querySelector('.layer-placeholder'); + const currLayer = stepInfo.target.querySelector(`.layer-${stepInfo.stepIndex}`); + const prevStepIndex = getPrevStepIndex(stepInfo); + const prevLayer = stepInfo.target.querySelector(`.layer-${prevStepIndex}`); + const miloLibs = getLibs('/libs'); + const { decorateDefaultLinkAnalytics } = await import(`${miloLibs}/martech/attributes.js`); + await handleImageTransition(stepInfo); + await loadAllImgs(currLayer.querySelectorAll('img[src*="media_"]')); + await decorateDefaultLinkAnalytics(currLayer); + if (prevStepIndex) stepInfo.target.classList.remove(`step-${stepInfo.stepList[prevStepIndex]}`); + if (clsLayer) clsLayer.remove(); + stepInfo.target.classList.add(`step-${stepInfo.stepName}`); + currLayer.classList.add('show-layer'); + if (currLayer === prevLayer) return; + prevLayer?.classList.remove('show-layer'); +} + +async function implementWorkflow(stepInfo) { + const currLayer = stepInfo.target.querySelector(`.layer-${stepInfo.stepIndex}`); + const layer = await stepInfo.stepInit(stepInfo); + if (currLayer) currLayer.replaceWith(layer); + else { + stepInfo.target.append(layer); + if (stepInfo.stepIndex === 0) await addLayerAnimation(stepInfo.target); + } + await handleLayerDisplay(stepInfo); + await handleNextStep(stepInfo); +} + +function checkRenderStatus(targetBlock, res, rej, etime, rtime) { + if (etime > 20000) { rej(); return; } + if (targetBlock.querySelector('.text') && targetBlock.querySelector('.image')) res(); + else setTimeout(() => checkRenderStatus(targetBlock, res, rej, etime + rtime), rtime); +} + +function intEnbReendered(targetBlock) { + return new Promise((res, rej) => { + try { + checkRenderStatus(targetBlock, res, rej, 0, 100); + } catch (err) { rej(); } + }); +} + +function decorateEnticementArrow(aa) { + const enticementArrow = aa.querySelector(':scope > p img[src*="svg"]'); + if (!enticementArrow) return; + const entP = enticementArrow.closest('p'); + const entTxt = createTag('div', { class: 'enticement-message' }, entP.textContent); + const enticement = createTag('div', { class: 'enticement-container' }); + enticementArrow.classList.add('enticement-svg'); + enticement.append(entTxt, enticementArrow); + entP.replaceWith(enticement); +} + +function decorateMobileHeading(intEnb) { + if (!intEnb.classList.contains('heading-top')) return; + const h = intEnb.querySelector('.text').querySelector('h1, h2, h3, h4, h5, h6'); + if (!h) return; + const htxt = h.textContent; + const hTxtTop = createTag('div', { class: 'mobile-top-title' }, htxt); + intEnb.querySelector('.image').prepend(hTxtTop); +} + +function createInteractiveArea(el, pic) { + const iArea = createTag('div', { class: 'interactive-holder show-image' }); + const newPic = pic.cloneNode(true); + const p = createTag('p', {}, newPic); + el.querySelector(':scope > div > div').prepend(p); + pic.querySelector('img').src = getImgSrc(pic); + [...pic.querySelectorAll('source')].forEach((s) => s.remove()); + const video = createTag('video'); + iArea.append(pic, video); + const clsLayer = createTag('div', { class: 'layer layer-placeholder show-layer' }); + iArea.append(clsLayer); + if (el.classList.contains('light')) iArea.classList.add('light'); + else if (el.classList.contains('dark')) iArea.classList.add('dark'); + return iArea; +} + +async function getTargetArea(el) { + const metadataSec = el.closest('.section'); + const intEnb = metadataSec.querySelector('.marquee, .aside'); + if (!intEnb) return null; + try { + intEnb.classList.add('interactive-enabled'); + await intEnbReendered(intEnb); + } catch (err) { return null; } + const assets = intEnb.querySelectorAll('.asset picture, .image picture'); + const container = assets[assets.length - 1].closest('p'); + const iArea = createInteractiveArea(el, assets[assets.length - 1]); + const assetArea = intEnb.querySelector('.asset, .image'); + if (container) container.replaceWith(iArea); + else assetArea.append(iArea); + decorateMobileHeading(intEnb); + decorateEnticementArrow(assetArea); + return iArea; +} + +async function renderLayer(stepInfo) { + stepInfo.openForExecution = new Promise((resolve, reject) => { + stepInfo.stepIndex = getNextStepIndex(stepInfo); + if (stepInfo.stepIndex === 0) stepInfo.displayPath = 0; + stepInfo.stepName = stepInfo.stepList[stepInfo.stepIndex]; + implementWorkflow(stepInfo) + .then(() => resolve()) + .catch(() => reject()); + }); +} + +function getWorkFlowInformation(el) { + let wfName = ''; + const intWorkFlowConfig = { + 'workflow-generate-crop': ['generate', 'selector-tray', 'crop', 'start-over'], + 'workflow-generate-repeat-crop': ['generate', 'selector-tray', 'generate', 'selector-tray', 'crop', 'start-over'], + 'workflow-hue-sat': ['slider-tray'], + }; + const wfNames = Object.keys(intWorkFlowConfig); + [...el.classList].forEach((cn) => { if (cn.match('workflow-')) wfName = cn; }); + if (wfName === 'workflow-genfill') { + const genArr = new Array(el.childElementCount - 1).fill('generate'); + genArr.push('start-over'); + return genArr; + } + if (wfNames.includes(wfName)) { + return intWorkFlowConfig[wfName]; + } + if (wfName) { + const stepReplace = { selectortray: 'selector-tray', startover: 'start-over' }; + const replaceNames = Object.keys(stepReplace); + const wfList = wfName.split('workflow-')[1].split('-'); + wfList.forEach((w, i) => { if (replaceNames.includes(w)) wfList[i] = stepReplace[w]; }); + return wfList; + } + return []; +} + +export default async function init(el) { + const workflow = getWorkFlowInformation(el); + if (!workflow.length) return; + const targetAsset = await getTargetArea(el); + if (!targetAsset) return; + const stepInfo = { + el, + stepIndex: -1, + stepName: '', + stepList: workflow, + stepConfigs: el.querySelectorAll(':scope > div'), + nextStepEvent: 'cc:interactive-switch', + target: targetAsset, + displayPath: 0, + openForExecution: Promise.resolve(true), + }; + await handleNextStep(stepInfo); + await renderLayer(stepInfo); + if (workflow.length === 1) return; + el.addEventListener('cc:interactive-switch', async () => { + await renderLayer(stepInfo); + }); +} diff --git a/creativecloud/features/interactive-components/crop/crop.css b/creativecloud/features/interactive-components/crop/crop.css new file mode 100644 index 000000000..f60bb2445 --- /dev/null +++ b/creativecloud/features/interactive-components/crop/crop.css @@ -0,0 +1,75 @@ +.interactive-enabled .step-crop .layer .gray-button.crop-button { + padding: 0 13px; + height: 48px; + margin: auto; +} + +@media screen and (min-width: 600px) { + .interactive-enabled .step-crop .layer .gray-button.crop-button { + height: 64px; + min-width: 145px; + gap: 16px; + margin: auto; + padding: 5px; + padding-right: 13px; + left: 47px; + right: auto; + bottom: 125px; + } + + [dir="rtl"] .interactive-enabled .step-crop .layer .gray-button.crop-button { + padding: 5px; + padding-left: 13px; + left: auto; + right: 47px; + } + + .interactive-enabled .step-crop .layer .gray-button.crop-button .crop-icon-container { + display: flex; + align-items: center; + justify-content: center; + height: 46px; + width: 46px; + border-radius: 3px; + border: 3px solid var(--prompt-input-fill); + background-color: var(--prompt-input-fill); + } + + .interactive-enabled.light .step-crop .layer .gray-button.crop-button .crop-icon-container, + .interactive-enabled .interactive-holder.light.step-crop .layer .gray-button.crop-button .crop-icon-container { + border: 3px solid var(--prompt-input-fill-light); + background-color: var(--prompt-input-fill-light); + } + + .interactive-enabled .interactive-holder.dark.step-crop .layer .gray-button.crop-button .crop-icon-container { + border: 3px solid var(--prompt-input-fill); + background-color: var(--prompt-input-fill); + } + + .interactive-enabled .step-crop .layer .gray-button.crop-button img { + width: 34px; + height: auto; + } + + .interactive-enabled .step-crop .layer .gray-button.crop-button:focus-visible, + .interactive-enabled .step-crop .layer .gray-button.crop-button:hover { + border: 3px solid transparent; + } + + .interactive-enabled .step-crop .layer .gray-button.crop-button:focus-visible .crop-icon-container, + .interactive-enabled .step-crop .layer .gray-button.crop-button:hover .crop-icon-container { + border-color: var(--prompt-highlight-color); + } +} + +@media screen and (min-width: 1200px) { + .interactive-enabled.row-reversed .step-crop .layer .gray-button.crop-button { + left: auto; + right: 47px; + } + + [dir="rtl"] .interactive-enabled.row-reversed .step-crop .layer .gray-button.crop-button { + left: 47px; + right: auto; + } +} diff --git a/creativecloud/features/interactive-components/crop/crop.js b/creativecloud/features/interactive-components/crop/crop.js new file mode 100644 index 000000000..50aa1b2da --- /dev/null +++ b/creativecloud/features/interactive-components/crop/crop.js @@ -0,0 +1,23 @@ +import { createTag } from '../../../scripts/utils.js'; + +export default async function stepInit(data) { + data.target.classList.add('step-crop'); + const config = data.stepConfigs[data.stepIndex]; + const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); + const cropCTA = createTag('a', { class: 'gray-button body-m crop-button', href: '#' }); + const svg = config.querySelector('img[src*=".svg"')?.closest('picture'); + if (svg) { + svg.insertAdjacentElement('afterend', svg.cloneNode(true)); + cropCTA.appendChild(createTag('div', { class: 'crop-icon-container' }, svg)); + } + if (config.textContent) cropCTA.appendChild(document.createTextNode(config.textContent.trim())); + cropCTA.addEventListener('click', async (e) => { + e.preventDefault(); + await data.openForExecution; + if (layer.classList.contains('disable-click')) return; + layer.classList.add('disable-click'); + data.el.dispatchEvent(new CustomEvent(data.nextStepEvent)); + }); + layer.append(cropCTA); + return layer; +} diff --git a/creativecloud/features/interactive-components/generate/generate.css b/creativecloud/features/interactive-components/generate/generate.css new file mode 100644 index 000000000..1409406d5 --- /dev/null +++ b/creativecloud/features/interactive-components/generate/generate.css @@ -0,0 +1,162 @@ +.interactive-enabled .step-generate .generate-prompt-button { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + font-weight: bold; + margin: auto; + padding: 0 5px; + width: calc(100% - 10px); +} + +.interactive-enabled .step-generate .generate-text-container { + display: flex; + align-items: center; + justify-content: space-between; + height: 48px; + border-radius: 8px 0 0 8px; + padding: 0 5px; + background-color: var(--prompt-text-border); + box-shadow: 0 10px 10px rgba(0, 0, 0, 0.32); + white-space: nowrap; + overflow: hidden; + width: 100%; +} + +[dir="rtl"] .interactive-enabled .step-generate .generate-text-container { + width: 100%; + border-radius: 0 8px 8px 0; +} + +.interactive-enabled .step-generate .generate-prompt-button .generate-text { + background-color: var(--prompt-input-fill); + color: var(--prompt-text-color); + display: inline-block; + height: 39px; + width: 100%; + line-height: 39px; + padding: 0; + padding-inline-start: 16px; + padding-inline-end: 16px; + border-radius: 3px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.interactive-enabled.light .generate-prompt-button .generate-text { + background-color: var(--prompt-btn-fill-light); + color: var(--prompt-text-color-light); +} + +.interactive-enabled .interactive-holder.dark .layer .generate-prompt-button .generate-text { + background-color: var(--prompt-input-fill); + color: var(--prompt-text-color); +} + +.interactive-enabled .interactive-holder.light .layer .generate-prompt-button .generate-text { + background-color: var(--prompt-btn-fill-light); + color: var(--prompt-text-color-light); +} + +.interactive-enabled .step-generate .generate-prompt-button .gray-button.generate-button { + position: relative; + align-items: center; + justify-content: center; + height: 48px; + gap: 2px; + border-radius: 0 8px 8px 0; + padding: 0 13px; + max-width: 50%; +} + +[dir="rtl"] .interactive-enabled .step-generate .generate-prompt-button .gray-button.generate-button { + border-radius: 8px 0 0 8px; +} + +@media screen and (min-width: 600px) { + .interactive-enabled .step-generate .generate-prompt-button { + position: absolute; + left: auto; + bottom: 64px; + width: auto; + max-width: unset; + padding: 0; + } + + .interactive-enabled.generate-right .step-generate .generate-prompt-button, + .interactive-enabled .step-generate .generate-right .generate-prompt-button { + left: auto; + right: -21.5%; + bottom: calc(50% - 80px); + } + + [dir="rtl"] .interactive-enabled.generate-right .step-generate .generate-prompt-button, + [dir="rtl"] .interactive-enabled .step-generate .generate-right .generate-prompt-button { + left: auto; + right: -21.5%; + } + + .interactive-enabled .step-generate .generate-text-container { + width: 215px; + min-width: 215px; + max-width: 215px; + height: 64px; + } + + [dir="rtl"] .interactive-enabled .step-generate .generate-text-container { + border-radius: 0 8px 8px 0; + } + + .interactive-enabled .step-generate .generate-prompt-button .generate-text { + height: 52px; + line-height: 52px; + padding: 0 16px; + text-align: start; + font-size: 21px; + } + + .interactive-enabled .step-generate .generate-prompt-button .gray-button.generate-button { + height: 64px; + gap: 16px; + padding: 0 21px; + width: auto; + min-width: auto; + max-width: unset; + } +} + @media screen and (min-width: 1200px) { + .interactive-enabled.row-reversed .step-generate .generate-text-container { + width: 218px; + max-width: 218px; + } + + .interactive-enabled.row-reversed.generate-right .step-generate .generate-prompt-button, + .interactive-enabled.row-reversed .step-generate .generate-right .generate-prompt-button { + flex-direction: row; + left: -21.5%; + right: auto; + } + + [dir="rtl"] .interactive-enabled.generate-right .step-generate .generate-prompt-button, + [dir="rtl"] .interactive-enabled .step-generate .generate-right .generate-prompt-button { + left: -21.5%; + right: auto; + } + + [dir="rtl"] .interactive-enabled.row-reversed.generate-right .step-generate .generate-prompt-button, + [dir="rtl"] .interactive-enabled.row-reversed .step-generate .generate-right .generate-prompt-button { + left: auto; + right: -21.5%; + } + + [dir="rtl"] .interactive-enabled .step-generate .generate-text-container, + .interactive-enabled .step-generate .generate-prompt-button .gray-button.generate-button { + border-radius: 0 8px 8px 0; + } + + .interactive-enabled .step-generate .generate-text-container, + [dir="rtl"] .interactive-enabled .step-generate .generate-prompt-button .gray-button.generate-button { + border-radius: 8px 0 0 8px; + } +} diff --git a/creativecloud/features/interactive-components/generate/generate.js b/creativecloud/features/interactive-components/generate/generate.js new file mode 100644 index 000000000..0e11aeeef --- /dev/null +++ b/creativecloud/features/interactive-components/generate/generate.js @@ -0,0 +1,32 @@ +import { createTag } from '../../../scripts/utils.js'; + +export default async function stepInit(data) { + data.target.classList.add('step-generate'); + const config = data.stepConfigs[data.stepIndex]; + const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); + const [searchText, btnText, position] = config.textContent.trim().split('|'); + if (position) layer.classList.add(`generate-${position.toLowerCase().trim()}`); + const genfillDiv = createTag('div', { class: 'generate-prompt-button body-m' }); + const searchBar = createTag('div', { class: 'generate-text' }, `${searchText}`); + const searchBarContainer = createTag('div', { class: 'generate-text-container' }, searchBar); + const generateBtn = createTag('a', { class: 'gray-button generate-button next-step', href: '#' }); + const analyticsHolder = createTag('div', { class: 'interactive-link-analytics-text' }, `${searchText} - `); + const svg = config.querySelector('img[src*=".svg"]')?.closest('picture'); + if (svg) { + svg.insertAdjacentElement('afterend', svg.cloneNode(true)); + generateBtn.appendChild(svg); + } + generateBtn.appendChild(analyticsHolder); + generateBtn.appendChild(document.createTextNode(btnText)); + genfillDiv.appendChild(searchBarContainer); + genfillDiv.appendChild(generateBtn); + layer.appendChild(genfillDiv); + generateBtn.addEventListener('click', async (e) => { + e.preventDefault(); + if (layer.classList.contains('disable-click')) return; + layer.classList.add('disable-click'); + await data.openForExecution; + data.el.dispatchEvent(new CustomEvent(data.nextStepEvent)); + }); + return layer; +} diff --git a/creativecloud/features/interactive-components/selector-tray/selector-tray.css b/creativecloud/features/interactive-components/selector-tray/selector-tray.css new file mode 100644 index 000000000..75f6840e6 --- /dev/null +++ b/creativecloud/features/interactive-components/selector-tray/selector-tray.css @@ -0,0 +1,125 @@ +.interactive-enabled .step-selector-tray .selector-tray { + position: absolute; + bottom: 0; + left: 0; + padding: 0 16px 12px; + display: flex; + flex-direction: column; + gap: 8px; + background: var(--prompt-input-fill); + border-radius: 0 0 8px 8px; + font-weight: bold; + box-sizing: border-box; + width: 100%; + color: var(--color-white); +} + +.interactive-enabled.light .step-selector-tray .selector-tray { + background: var(--prompt-btn-fill-light); + color: var(--prompt-text-color-light); +} + +.interactive-enabled .interactive-holder.dark.step-selector-tray .selector-tray { + background: var(--prompt-input-fill); + color: var(--color-white); +} + +.interactive-enabled .interactive-holder.light.step-selector-tray .selector-tray { + background: var(--prompt-btn-fill-light); + color: var(--prompt-text-color-light); +} + +.interactive-enabled .step-selector-tray .selector-tray .tray-title { + font-size: var(--type-body-m-size); + line-height: var(--type-body-m-lh); +} + +.interactive-enabled .step-selector-tray .selector-tray .tray-items { + display: flex; + flex-direction: row; + gap: 14px; + justify-content: space-between; +} + +.interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img { + display: block; + width: 30%; + height: 80px; + background-repeat: no-repeat; + background-size: cover; + background-position: center; + outline: none; +} + +.interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img .preload-img { + display: none; +} + +.interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img .tray-thumbnail-outline { + display: none; + height: calc(100% - 10px); + width: calc(100% - 10px); + border: 5px solid var(--prompt-highlight-color); +} + +.interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img.thumbnail-selected .tray-thumbnail-outline, +.interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img:focus-visible .tray-thumbnail-outline, +.interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img:hover .tray-thumbnail-outline { + display: block; +} + +@media screen and (min-width: 600px) { + .interactive-enabled .step-selector-tray .selector-tray { + bottom: 70px; + left: 56%; + right: auto; + padding: 24px 16px 16px; + gap: 16px; + border-radius: 9px; + box-shadow: 0 10px 10px rgba(0, 0, 0, 0.32); + box-sizing: content-box; + width: auto; + } + + [dir="rtl"] .interactive-enabled .step-selector-tray .selector-tray { + left: auto; + right: 56%; + } + + .interactive-enabled .step-selector-tray .selector-tray .tray-items { + gap: 16px + } + + .interactive-enabled .step-selector-tray .selector-tray .tray-title { + font-size: var(--type-body-xl-size); + line-height: var(--type-body-xl-lh); + } + + .interactive-enabled.selector-vertical .step-selector-tray .selector-tray .tray-items, + .interactive-enabled .step-selector-tray .selector-tray .tray-items.vertical { + flex-direction: column; + } + + .interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img { + width: 80px; + height: 120px; + } + + .interactive-enabled .step-selector-tray .selector-tray .tray-items a.tray-thumbnail-img .tray-thumbnail-outline { + height: calc(100% - 12px); + width: calc(100% - 12px); + border: 6px solid var(--prompt-highlight-color); + } +} + +@media screen and (min-width: 1200px) { + .interactive-enabled.row-reversed .step-selector-tray .selector-tray { + left: auto; + right: 56%; + } + + [dir="rtl"] .interactive-enabled.row-reversed .step-selector-tray .selector-tray { + right: auto; + left: 56%; + } +} diff --git a/creativecloud/features/interactive-components/selector-tray/selector-tray.js b/creativecloud/features/interactive-components/selector-tray/selector-tray.js new file mode 100644 index 000000000..c138d524a --- /dev/null +++ b/creativecloud/features/interactive-components/selector-tray/selector-tray.js @@ -0,0 +1,89 @@ +import { createTag } from '../../../scripts/utils.js'; +import { handleImageTransition, getImgSrc } from '../../../blocks/interactive-metadata/interactive-metadata.js'; + +function getTrayConfig(data) { + const dpth = data.displayPath; + const allUls = data.stepConfigs[data.stepIndex].querySelectorAll('ul'); + const configUl = (dpth >= 0 && allUls.length > dpth) ? allUls[dpth] : allUls[0]; + return configUl; +} + +function getStartingPathIdx(data) { + let pathIdx = 0; + const dpth = data.displayPath; + const allUls = data.stepConfigs[data.stepIndex].querySelectorAll('ul'); + if (allUls.length < dpth) return pathIdx; + for (let i = 0; i < dpth; i += 1) pathIdx += allUls[i].querySelectorAll('li').length; + return pathIdx; +} + +function createSelectorThumbnail(pic, pathId, displayImg) { + const src = getImgSrc(pic); + const outline = createTag('div', { class: 'tray-thumbnail-outline' }); + const a = createTag('a', { class: 'tray-thumbnail-img', href: '#' }, outline); + a.style.backgroundImage = `url(${src})`; + [a.dataset.dispSrc, a.dataset.dispAlt] = displayImg; + a.dataset.dispPth = pathId; + const img = createTag('img', { class: 'preload-img', src }); + const analyticsHolder = createTag('div', { class: 'interactive-link-analytics-text' }, pic.querySelector('img').alt); + a.append(img, analyticsHolder); + if (pathId === 0) a.classList.add('thumbnail-selected'); + return a; +} + +function attachThumbnailEvents(a, data, layer) { + ['mouseover', 'touchstart', 'focus'].forEach((event) => { + a.addEventListener(event, (e) => { + e.target.closest('.tray-items')?.querySelector('.thumbnail-selected')?.classList.remove('thumbnail-selected'); + }); + }); + a.addEventListener('click', async (e) => { + e.preventDefault(); + if (layer.classList.contains('disable-click')) return; + layer.classList.add('disable-click'); + const curra = e.target.nodeName === 'A' ? e.target : e.target.closest('a'); + await data.openForExecution; + data.displayPath = parseInt(curra.dataset.dispPth, 10); + const trObj = { src: curra.dataset.dispSrc, alt: curra.dataset.dispAlt, useCfg: true }; + await handleImageTransition(data, trObj); + data.el.dispatchEvent(new CustomEvent(data.nextStepEvent)); + e.target.closest('.tray-items').querySelector('a.tray-thumbnail-img').classList.add('thumbnail-selected'); + }); +} + +function selectorTrayWithImgs(layer, data) { + const selectorTray = createTag('div', { class: 'body-s selector-tray' }); + const trayItems = createTag('div', { class: 'tray-items' }); + const configUl = getTrayConfig(data); + const pics = configUl.querySelectorAll('picture'); + let pathIdx = getStartingPathIdx(data); + let displayImg = null; + [...pics].forEach((pic, idx) => { + if (idx % 2 === 0) { + displayImg = [getImgSrc(pic), pic.querySelector('img').alt]; + return; + } + const a = createSelectorThumbnail(pic, pathIdx, displayImg); + trayItems.append(a); + pathIdx += 1; + attachThumbnailEvents(a, data, layer); + }); + selectorTray.append(trayItems); + return selectorTray; +} + +export default async function stepInit(data) { + data.target.classList.add('step-selector-tray'); + const config = data.stepConfigs[data.stepIndex]; + const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); + const title = config.querySelector('p:first-child'); + let trayTitle = null; + if (title) trayTitle = createTag('div', { class: 'tray-title' }, title.innerText.trim()); + const trayConfig = config.querySelectorAll('ul > li'); + const isGenerateTray = [...trayConfig].filter((li) => (li.querySelector('img[src*="media_"]').length >= 2)); + let selectorTray = null; + if (isGenerateTray) selectorTray = selectorTrayWithImgs(layer, data); + if (title) selectorTray.prepend(trayTitle); + layer.append(selectorTray); + return layer; +} diff --git a/creativecloud/features/interactive-components/slider-tray/slider-tray.css b/creativecloud/features/interactive-components/slider-tray/slider-tray.css new file mode 100644 index 000000000..f4842bb6a --- /dev/null +++ b/creativecloud/features/interactive-components/slider-tray/slider-tray.css @@ -0,0 +1,358 @@ +.interactive-enabled .step-slider-tray .sliderTray { + display: flex; + flex-direction: column; + position: relative; + justify-content: space-between; + align-items: center; + gap: 15px; + transform: none; + border-radius: 0; + top: 0; + width: 100%; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu { + border-radius: 0 0 8px 8px; + width: 100%; + box-sizing: border-box; + padding: 6px 16px 16px; + gap: 12px; + display: flex; + flex-direction: column; + background: var(--prompt-input-fill); + justify-content: space-between; + color: var(--color-white); + font-weight: bold; + box-shadow: 0 10px 10px rgba(0, 0, 0, 0.32); +} + +.interactive-enabled.light .step-slider-tray .sliderTray .menu { + background: var(--prompt-btn-fill-light); + color: var(--color-black); +} + +.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu { + background: var(--prompt-input-fill); + color: var(--color-white); +} + +.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu { + background: var(--prompt-btn-fill-light); + color: var(--color-black); +} + +.interactive-enabled .step-slider-tray .sliderTray .menu label { + margin: 10px 0 0; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle { + position: absolute; + block-size: 20px; + inline-size: 20px; + background-color: var(--prompt-btn-fill-light); + border-radius: 50%; + top: -9px; + inset-block-start: 50%; + inset-inline-start: 50%; + transform: translate(-38%, -50%); + z-index: 1; +} + +.interactive-enabled.light .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle { + background-color: var(--color-black); +} + +.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu .sliderContainer .outerCircle { + background-color: var(--prompt-btn-fill-light); +} + +.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu .sliderContainer .outerCircle { + background-color: var(--color-black); +} + +[dir="rtl"] .interactive-enabled:not(.row-reversed) .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle, +[dir="rtl"] .interactive-enabled.row-reversed .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle { + transform: translate(38%, -50%); +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle::before { + border-radius: 100%; + content: ""; + display: block; + inset-block-start: 50%; + inset-inline-start: 50%; + position: absolute; + transform: translate(-50%, -50%); + transition: box-shadow 300ms ease-out 0s, inline-size 300ms ease-out 0s, block-size 300ms ease-out 0s, opacity 300ms ease-out 0s; + block-size: 40px; + inline-size: 40px; + box-shadow: 0 0 0 8px #3892f3; + opacity: 0; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .showOuterBorder::before { + opacity: 0.4; +} + +[dir="rtl"] .interactive-enabled:not(.row-reversed) .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle::before, +[dir="rtl"] .interactive-enabled.row-reversed .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle::before { + transform: translate(50%, -50%); +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .saturation, +.interactive-enabled .step-slider-tray .sliderTray .menu .hue { + position: relative; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: 0.5em; + border: none; + background-color: rgba(0, 0, 0, 0.1); + height: 3px; + display: block; + outline: none; + transition: color 0.05s linear; + width: 100%; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .saturation { + background: linear-gradient(to right, black, red); +} + +[dir='rtl'] .interactive-enabled .step-slider-tray .sliderTray .menu .saturation { + background: linear-gradient(to left, darkgray, red); +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .hue { + background: linear-gradient(to right, red, yellow, lime, aqua, blue, fuchsia, red); +} + +[dir='rtl'] .interactive-enabled .step-slider-tray .sliderTray .menu .hue { + background: linear-gradient(to left, red, yellow, lime, aqua, blue, fuchsia, red); +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .options::-webkit-slider-thumb, +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .options::-webkit-slider-thumb { + height: 20px; + width: 20px; + position: relative; + border-radius: 2em; + -webkit-appearance: none; + appearance: none; + background: transparent; + cursor: pointer; + cursor: move; + cursor: grab; + cursor: -webkit-grab; + z-index: 2; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .options[type="range"]:focus { + outline: none; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .options { + width: 100%; + opacity: 0; + position: absolute; + top: -9px; + z-index: 2; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .animate::before, +[dir='rtl'] .interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .animate::before, +.interactive-enabled.row-reversed .step-slider-tray .sliderTray .menu .sliderContainer .animate::before, +[dir='rtl'] .interactive-enabled.row-reversed .step-slider-tray .sliderTray .menu .sliderContainer .animate::before { + opacity: 1; + box-shadow: 0 0 0 4px #3892f3; + inline-size: 25px; + block-size: 25px; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .animateout::before, +[dir='rtl'] .interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .animateout::before, +.interactive-enabled.row-reversed .step-slider-tray .sliderTray .menu .sliderContainer .animateout::before { + opacity: 0; + box-shadow: 0 0 0 8px #3892f3; + inline-size: 40px; + block-size: 40px; +} + +.interactive-enabled .step-slider-tray .sliderTray .uploadButton:hover, +.interactive-enabled .step-slider-tray .sliderTray .uploadButton.focusUploadButton { + border: 3px solid #1273E6; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .sliderContainer .outerCircle.focusUploadButton { + border: 4px solid #3892f3; +} + +.interactive-enabled .step-slider-tray .sliderTray .uploadButton > input { + opacity: 0; + position: absolute; + z-index: 2; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.interactive-enabled .step-slider-tray .sliderTray .uploadButton { + display: none; +} + +.interactive-enabled .step-slider-tray .continueButton { + display: none; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .uploadButtonMobile { + display: inline-flex; + box-sizing: content-box; + margin: 16px auto 0; + background: var(--prompt-btn-fill-light); + color: var(--color-black) !important; + width: auto; + gap: 8px; + padding: 5px 12px 5px 8px; + max-width: fit-content; + border-radius: 8px; + align-items: center; + max-height: 32px; + font-size: 21px; + border: 3px solid; + text-decoration: none; + position: relative; +} + +.interactive-enabled.light .step-slider-tray .sliderTray .menu .uploadButtonMobile { + background: var(--prompt-btn-fill); + color: var(--color-white) !important; +} + +.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu .uploadButtonMobile { + background: var(--prompt-btn-fill-light); + color: var(--color-black) !important; +} + +.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu .uploadButtonMobile { + background: var(--prompt-btn-fill); + color: var(--color-white) !important; +} + +.interactive-enabled .step-slider-tray .sliderTray .uploadButton.uploadButtonMobile:hover, +.interactive-enabled .step-slider-tray .sliderTray .uploadButton.uploadButtonMobile.focusUploadButton { + border: 3px solid #1273E6; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu .uploadButtonMobile.hide { + display: none; +} + +.interactive-enabled .step-slider-tray .sliderTray .svg-icon-container { + height: 18px; + width: 18px; +} + +@media screen and (min-width: 600px) { + .interactive-enabled .step-slider-tray .sliderTray, + [dir="rtl"] .interactive-enabled.row-reversed .step-slider-tray .sliderTray { + position: absolute; + top: 45%; + gap: 15px; + transform: translateY(-50%); + width: 260px; + right: -20%; + } + + [dir="rtl"] .interactive-enabled:not(.row-reversed) .step-slider-tray .sliderTray, + .interactive-enabled.row-reversed .step-slider-tray .sliderTray { + left: -20%; + right: auto; +} + +.interactive-enabled .step-slider-tray .sliderTray .menu { + border-radius: 8px; + padding: 14px 24px 28px; + bottom: inherit; + gap: 12px; + width: 260px; + box-sizing: border-box; + } + + .interactive-enabled .step-slider-tray .sliderTray .uploadButton { + top: 70%; + display: flex; + gap: 16px; + background: var(--prompt-input-fill); + border-radius: 8px; + text-decoration: none; + color: var(--prompt-btn-fill-light); + font-weight: bold; + padding: 6px 21px; + align-items: center; + justify-content: center; + width: 260px; + border: 3px solid transparent; + box-sizing: border-box; + box-shadow: 0 10px 10px rgba(0, 0, 0, 0.32); + font-size: 21px; + height: 64px; + position: relative; + } + + .interactive-enabled.light .step-slider-tray .sliderTray .uploadButton { + background: var(--prompt-btn-fill-light); + color: var(--color-black); + } + + .interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .uploadButton { + background: var(--prompt-input-fill); + color: var(--color-white); + } + + .interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .uploadButton { + background: var(--prompt-btn-fill-light); + color: var(--color-black); + } + + .interactive-enabled .step-slider-tray .sliderTray .menu .uploadButtonMobile { + display: none; + } + + .interactive-enabled .step-slider-tray .sliderTray .svg-icon-container { + display: flex; + align-items: center; + justify-content: center; + height: 34px; + width: 34px; + border-radius: 3px; + } + + .interactive-enabled .step-slider-tray .sliderTray .continueButton { + position: absolute; + top: 85%; + display: flex; + gap: 16px; + background: #1273E6; + border-radius: 8px; + text-decoration: none; + color: var(--prompt-btn-fill-light) !important; + font-weight: bold; + padding: 12px 24px; + align-items: center; + justify-content: center; + width: 60%; + left: 50%; + transform: translateX(-50%); + cursor: pointer; + } + + .interactive-enabled .step-slider-tray .sliderTray .continueButton.hide { + display: none; + } + + .interactive-enabled .step-slider-tray .sliderTray .continueButton:hover { + color: #1273E6 !important; + background: var(--prompt-btn-fill-light); + } +} diff --git a/creativecloud/features/interactive-components/slider-tray/slider-tray.js b/creativecloud/features/interactive-components/slider-tray/slider-tray.js new file mode 100644 index 000000000..57769363b --- /dev/null +++ b/creativecloud/features/interactive-components/slider-tray/slider-tray.js @@ -0,0 +1,263 @@ +/* eslint-disable no-case-declarations */ +/* eslint-disable no-use-before-define */ +import { createTag } from '../../../scripts/utils.js'; +import defineDeviceByScreenSize from '../../../scripts/decorate.js'; + +export default async function stepInit(data) { + const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); + await createSelectorTray(data, layer); + sliderEvent(data.target, layer); + uploadImage(data.target, layer); + return layer; +} + +async function createSelectorTray(data, layer) { + const sliderTray = createTag('div', { class: 'sliderTray' }); + const menu = createTag('div', { class: 'menu' }); + const config = data.stepConfigs[data.stepIndex]; + const options = config.querySelectorAll(':scope > div .icon'); + [...options].forEach((o) => { handleInput(o, sliderTray, menu, layer); }); + layer.append(sliderTray); + observeSliderTray(sliderTray, data.target, menu); +} + +function handleInput(option, sliderTray, menu, layer) { + let inputType = option.classList[1].split('icon-')[1]; + const sliderType = inputType.split('-')[0]; + if (inputType.includes('slider')) inputType = 'slider'; + const sibling = option.nextSibling; + const text = sibling.nodeValue.trim(); + let picture = ''; + if (sibling.nextSibling && sibling.nextSibling.tagName === 'PICTURE') { + picture = sibling.nextSibling; + } + switch (inputType) { + case 'slider': + createSlider(sliderType, text, menu, sliderTray); + break; + case 'upload': + createUploadButton(text, picture, sliderTray, menu); + break; + case 'upload-ps': + createUploadPSButton(text, picture, layer); + break; + default: + window.lana.log(`Unknown input type: ${inputType}`); + break; + } +} + +function observeSliderTray(sliderTray, targets) { + const options = { threshold: 0.7 }; + const io = new IntersectionObserver((entries, observer) => { + entries.forEach((entry) => { + if (!entry.isIntersecting) return; + const menu = sliderTray.querySelector('.menu'); + const outerCircle = menu.querySelector('.outerCircle'); + outerCircle.classList.add('showOuterBorder'); + setTimeout(() => { animateSlider(menu, targets); }, 800); + observer.unobserve(entry.target); + }); + }, options); + io.observe(sliderTray); +} + +function createSlider(sliderType, details, menu, sliderTray) { + const [label, min, max] = details.split('|').map((item) => item.trim()); + const sliderLabel = createTag('label', { for: `${sliderType}` }, label); + const sliderContainer = createTag('div', { class: `sliderContainer ${sliderType.toLowerCase()}` }); + const outerCircle = createTag('a', { class: 'outerCircle', href: '#', tabindex: '-1' }); + const analyticsHolder = createTag('div', { class: 'interactive-link-analytics-text' }, `Adjust ${sliderType} slider`); + const input = createTag('input', { + type: 'range', + min, + max, + class: `options ${sliderType.toLowerCase()}-input`, + value: `${sliderType === 'hue' ? '0' : '180'}`, + }); + outerCircle.append(analyticsHolder); + sliderContainer.append(input, outerCircle); + menu.append(sliderLabel, sliderContainer); + sliderTray.append(menu); + outerCircle.addEventListener('click', (e) => { + e.preventDefault(); + }); + applyAccessibility(input, outerCircle); +} + +function createUploadButton(details, picture, sliderTray, menu) { + const currentVP = defineDeviceByScreenSize().toLocaleLowerCase(); + const btn = createTag('input', { class: 'inputFile', type: 'file', accept: 'image/*' }); + const labelBtn = createTag('a', { class: `uploadButton body-${currentVP === 'mobile' ? 'm' : 'xl'}` }, details); + const analyticsHolder = createTag('div', { class: 'interactive-link-analytics-text' }, `${details}`); + labelBtn.append(btn, analyticsHolder); + appendSVGToButton(picture, labelBtn); + const clone = labelBtn.cloneNode(true); + clone.classList.add('uploadButtonMobile'); + menu.append(clone); + sliderTray.append(labelBtn); + applyAccessibility(btn, labelBtn); +} + +function applyAccessibility(inputEle, target) { + let tabbing = false; + document.addEventListener('keydown', () => { + tabbing = true; + inputEle.addEventListener('focus', () => { + if (tabbing) { + target.classList.add('focusUploadButton'); + } + }); + inputEle.addEventListener('blur', () => { + target.classList.remove('focusUploadButton'); + }); + }); + document.addEventListener('keyup', () => { + tabbing = false; + }); +} + +function createUploadPSButton(details, picture, layer) { + const btn = createTag('a', { class: 'continueButton body-xl hide' }, details); + appendSVGToButton(picture, btn); + layer.append(btn); +} + +function appendSVGToButton(picture, button) { + if (!picture) return; + const svg = picture.querySelector('img[src*=svg]'); + if (!svg) return; + const svgClone = svg.cloneNode(true); + const svgCTACont = createTag('div', { class: 'svg-icon-container' }); + svgCTACont.append(svgClone); + button.prepend(svgCTACont); +} + +function sliderEvent(media, layer) { + let hue = 0; + let saturation = 100; + ['hue', 'saturation'].forEach((sel) => { + const sliderEl = layer.querySelector(`.${sel.toLowerCase()}-input`); + sliderEl.addEventListener('input', () => { + const image = media.querySelector('.interactive-holder picture > img'); + const { value } = sliderEl; + const outerCircle = sliderEl.nextSibling; + const rect = sliderEl.getBoundingClientRect(); + const value1 = (value - sliderEl.min) / (sliderEl.max - sliderEl.min); + const thumbOffset = value1 * (rect.width - outerCircle.offsetWidth); + const interactiveBlock = media.closest('.marquee') || media.closest('.aside'); + const isRowReversed = interactiveBlock.classList.contains('.row-reversed'); + if ((document.dir === 'rtl' || isRowReversed)) { + outerCircle.style.right = `${thumbOffset + 8}px`; + } else { + outerCircle.style.left = `${thumbOffset + 8}px`; + } + switch (sel.toLowerCase()) { + case ('hue'): + hue = value; + break; + case ('saturation'): + saturation = value; + break; + default: + break; + } + image.style.filter = `hue-rotate(${hue}deg) saturate(${saturation}%)`; + }); + sliderEl.addEventListener('change', () => { + const outerCircle = sliderEl.nextSibling; + outerCircle.click(); + }); + }); +} + +function uploadImage(media, layer) { + layer.querySelectorAll('.uploadButton').forEach((btn) => { + const analyticsBtn = btn.querySelector('.interactive-link-analytics-text'); + btn.addEventListener('cancel', () => { + cancelAnalytics(btn); + }); + btn.addEventListener('change', (event) => { + const image = media.querySelector('picture > img'); + const file = event.target.files[0]; + if (file) { + const sources = image.querySelectorAll('source'); + sources.forEach((source) => source.remove()); + const imageUrl = URL.createObjectURL(file); + image.src = imageUrl; + analyticsBtn.innerHTML = 'Upload Button'; + const continueBtn = layer.querySelector('.continueButton'); + if (continueBtn) { + continueBtn.classList.remove('hide'); + } + } else { + cancelAnalytics(btn); + } + }); + }); +} + +function cancelAnalytics(btn) { + const x = (e) => { + e.preventDefault(); + }; + btn.addEventListener('click', x); + const cancelEvent = new Event('click', { detail: { message: 'Cancel button clicked in file dialog' } }); + btn.setAttribute('daa-ll', 'Cancel Upload'); + btn.dispatchEvent(cancelEvent); + btn.removeEventListener('click', x); + btn.setAttribute('daa-ll', 'Upload Image'); +} + +function animateSlider(menu, target) { + const option = menu.querySelector('.options'); + const aobj = { interrupted: false }; + const outerCircle = option.nextSibling; + outerCircle.classList.add('animate'); + ['mousedown', 'touchstart'].forEach((e) => { + option.closest('.sliderTray').addEventListener(e, () => { + aobj.interrupted = true; + outerCircle.classList.remove('showOuterBorder', 'animate', 'animateout'); + }, { once: true }); + }); + outerCircle.addEventListener('transitionend', () => { + setTimeout(() => { + const min = parseInt(option.min, 10); + const max = parseInt(option.max, 10); + const middle = (min + max) / 2; + sliderScroll(option, middle, max, 1200, outerCircle, target, aobj); + }, 500); + }, { once: true }); +} + +function sliderScroll(slider, start, end, duration, outerCircle, target, aobj) { + let current = start; + let step = ((end - start) / duration) * 10; + let direction = 1; + function stepAnimation() { + slider.value = current; + current += step; + if (aobj.interrupted) return; + slider.dispatchEvent(new Event('input', { bubbles: true })); + if ((step > 0 && current >= (start + 70)) || (step < 0 && current >= (start + 70))) { + step = -step; + setTimeout(stepAnimation, 10); + } else if ((step > 0 && current <= (start - 70)) || (step < 0 && current <= (start - 70))) { + step = -step; + setTimeout(stepAnimation, 10); + direction = -1; + } else if (current === start && direction === -1) { + slider.value = current; + const image = target.querySelector('picture > img'); + image.style.filter = `hue-rotate(${0}deg)`; + setTimeout(() => { + outerCircle.classList.remove('animate'); + outerCircle.classList.add('animateout'); + }, 500); + slider.dispatchEvent(new Event('input', { bubbles: true })); + } else { + setTimeout(stepAnimation, 10); + } + } + setTimeout(stepAnimation, 10); +} diff --git a/creativecloud/features/interactive-components/start-over/start-over.css b/creativecloud/features/interactive-components/start-over/start-over.css new file mode 100644 index 000000000..21e03c97e --- /dev/null +++ b/creativecloud/features/interactive-components/start-over/start-over.css @@ -0,0 +1,20 @@ +.interactive-enabled .step-start-over .layer .gray-button.start-over-button { + display: none; + padding: 0 21px 0 13px; + height: 48px; + transition: display 300ms; +} + +@media screen and (min-width: 600px) { + .interactive-enabled .step-start-over .layer .gray-button.start-over-button { + padding: 0 21px; + height: 64px; + bottom: 64px; + left: auto; + gap: 16px; + } + + .interactive-enabled.generate-right .step-start-over .layer .gray-button.start-over-button { + bottom: 32px; + } +} diff --git a/creativecloud/features/interactive-components/start-over/start-over.js b/creativecloud/features/interactive-components/start-over/start-over.js new file mode 100644 index 000000000..25e5aec3c --- /dev/null +++ b/creativecloud/features/interactive-components/start-over/start-over.js @@ -0,0 +1,44 @@ +import { createTag } from '../../../scripts/utils.js'; + +function btnLoadDelay(layer, button, delay, once = true) { + const io = new IntersectionObserver((entries, observer) => { + entries.forEach(async (entry) => { + if (entry.isIntersecting) { + if (once) observer.unobserve(entry.target); + setTimeout(() => { button.style.display = 'flex'; }, parseInt(delay, 10)); + } + }); + }); + io.observe(layer); +} + +export default async function stepInit(data) { + data.target.classList.add('step-start-over'); + const config = data.stepConfigs[data.stepIndex]; + const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); + const startOverCTA = createTag('a', { class: 'gray-button start-over-button body-m next-step', href: '#' }); + const svg = config.querySelector('img[src*=".svg"]')?.closest('picture'); + if (svg) { + svg.insertAdjacentElement('afterend', svg.cloneNode(true)); + startOverCTA.append(svg.closest('picture').cloneNode(true)); + } + const lastp = config.querySelector(':scope > div > p:last-child'); + const btnConfig = lastp.textContent.trim(); + const btnLink = lastp.querySelector('a'); + const [btnText, delay] = btnConfig.split('|'); + if (btnText) startOverCTA.appendChild(document.createTextNode(btnText.trim())); + if (btnLink) startOverCTA.href = btnLink.href; + if (!btnLink) { + startOverCTA.addEventListener('click', async (e) => { + e.preventDefault(); + if (layer.classList.contains('disable-click')) return; + layer.classList.add('disable-click'); + await data.openForExecution; + data.el.dispatchEvent(new CustomEvent(data.nextStepEvent)); + }); + } + if (delay) btnLoadDelay(layer, startOverCTA, delay); + else startOverCTA.style.display = 'flex'; + layer.append(startOverCTA); + return layer; +} diff --git a/creativecloud/scripts/utils.js b/creativecloud/scripts/utils.js index 12768295f..3b753f87f 100644 --- a/creativecloud/scripts/utils.js +++ b/creativecloud/scripts/utils.js @@ -46,7 +46,7 @@ export const [setLibs, getLibs] = (() => { const miloLibs = setLibs('/libs'); const { createTag, localizeLink, getConfig, loadStyle } = await import(`${miloLibs}/utils/utils.js`); -export { createTag, localizeLink }; +export { createTag, loadStyle, localizeLink }; function getDecorateAreaFn() { let lcpImgSet = false; diff --git a/creativecloud/styles/styles.css b/creativecloud/styles/styles.css index 7405c694f..767c7ec84 100644 --- a/creativecloud/styles/styles.css +++ b/creativecloud/styles/styles.css @@ -1,13 +1,4 @@ -/* - * Put project specific base styles here. - * - * Note: The proect does not load this file. - * You will need to load these using scripts.js. - * - * - */ - - .reading-width { +.reading-width { max-width: 600px; margin: auto; padding: 20px; diff --git a/test/blocks/interactive-metadata/interactive-metadata.test.js b/test/blocks/interactive-metadata/interactive-metadata.test.js new file mode 100644 index 000000000..c281e889f --- /dev/null +++ b/test/blocks/interactive-metadata/interactive-metadata.test.js @@ -0,0 +1,72 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: './mocks/interactive-metadata.html' }); +const { setLibs } = await import('../../../creativecloud/scripts/utils.js'); +const { default: init } = await import('../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); + +describe('interactive metadata', () => { + let im = null; + let ib = null; + let genfillIm = null; + let genfillMarquee = null; + + before(async () => { + setLibs('https://milo.adobe.com/libs'); + im = document.querySelector('.interactive-metadata'); + ib = document.querySelector('.marquee'); + genfillIm = document.querySelector('.interactive-metadata.workflow-genfill'); + genfillMarquee = genfillIm.closest('.section').querySelector('.marquee'); + await init(im); + await init(genfillIm); + }); + it('interactive metadata should exist', () => { + expect(im).to.exist; + }); + it('should make the previous block interactive-enabled', () => { + expect(ib).to.exist; + expect(ib.classList.contains('interactive-enabled')).to.be.true; + }); + it('should create a workflow', () => { + let hasWorkflowClass = false; + im.classList.forEach((className) => { + if (className.startsWith('workflow-')) { + hasWorkflowClass = true; + } + }); + expect(hasWorkflowClass).to.be.true; + }); + it('should render next selector tray', async () => { + im.dispatchEvent(new CustomEvent('cc:interactive-switch')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(ib.querySelector('.interactive-holder.step-selector-tray')).to.exist; + }); + it('should render next crop layer', async () => { + im.dispatchEvent(new CustomEvent('cc:interactive-switch')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(ib.querySelector('.interactive-holder.step-crop')).to.exist; + }); + it('should render next start-over layer', async () => { + im.dispatchEvent(new CustomEvent('cc:interactive-switch')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(ib.querySelector('.interactive-holder.step-start-over')).to.exist; + }); + it('should render next generate layer', async () => { + im.dispatchEvent(new CustomEvent('cc:interactive-switch')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(ib.querySelector('.interactive-holder.step-generate')).to.exist; + }); + it('Genfill: should render generate layer', () => { + expect(genfillMarquee.querySelector('.interactive-holder.step-generate')).to.exist; + }); + it('Genfill: should render generate layer', async () => { + genfillIm.dispatchEvent(new CustomEvent('cc:interactive-switch')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(genfillMarquee.querySelector('.interactive-holder.step-generate')).to.exist; + }); + it('Genfill: should render start over layer', async () => { + genfillIm.dispatchEvent(new CustomEvent('cc:interactive-switch')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(genfillMarquee.querySelector('.interactive-holder.step-start-over')).to.exist; + }); +}); diff --git a/test/blocks/interactive-metadata/mocks/assets/icon_.svg b/test/blocks/interactive-metadata/mocks/assets/icon_.svg new file mode 100644 index 000000000..bdbc39e40 --- /dev/null +++ b/test/blocks/interactive-metadata/mocks/assets/icon_.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/test/blocks/interactive-metadata/mocks/assets/media_.png b/test/blocks/interactive-metadata/mocks/assets/media_.png new file mode 100644 index 0000000000000000000000000000000000000000..4faad891dd0ec0155689ff31761997b5a2b94abc GIT binary patch literal 1346 zcmV-I1-<%-P)ZMEF8w2uHrD^A&3O8Uer!fh}!z)2zBzwXh%F5<13# z(alu1OUmn!%z$~JraJu&0C@^~h`J~IAhdUu-836!26r;m?K9Oi9U?m~#Lic==7k!y z&6Dh=+4qn$xQ)Lh!B_ws{ZCXKIwDWBq@4vveo3QA3ELP$F$3xp9XY`xqiq*l^P>pe z#?P+XK9-_YvgZ3Z*I)4;#v#m19u{9;JY#NoQsaH4W z?TO$DVCa00humPsxk)QAY#J4{Li{%9&p`xlU(gG|HuD||dDR>((_#c~wW)|r4{ZkMcy)zJ@nU+W>?EN(~5aB~O z`tTTfB!Z43muM_ixd7PvtB?NDd5Rn!J0Bz^X%1;hG6+e^hbKx>UJwL95ClOG1VIo4 zK@bE>uzs-V(wG-&D*S$rJK+2~2AAFkrpl`cS@KB)*gIag(#5R|Dz7Gq_;&O~gI{_a zY3`9#za3#)BT_vB=7n01;QX6@c{Pcgq-PgkH_dt}yL-$QM3b31I+aZp)a=#Utscx;MIdRP&5u3e>a!+8p2kfTV9)3IYTtS2D44f&p zp!&q}F_CPt2(X)G>zZq8$*}4$H2A2=aqv(PZEHa;0FLA+qv3Wp&2k(F=jT32wmD`% z-Jp(&{opp{5-M%m90GhZXltXsjPu+xZQC3IT>80UM&Q|+p-KIlFg19jOK3O*xaJZi zaySI|YtYul7I)5b&$MlG2rx9LAbBJ1OAwr)kNDq&F{BVBI5o~xw>IhwI7QaB%@zy7 zP(;`lE4OOIRy=54sQqJj995_OebsHm_7s$#r20h+9cNP8+%a)Jq#F>;A0gkY~Ed>Dn1BoiN7#^%+1poj507*qoM6N<$ Eg2UEYg#Z8m literal 0 HcmV?d00001 diff --git a/test/blocks/interactive-metadata/mocks/interactive-metadata.html b/test/blocks/interactive-metadata/mocks/interactive-metadata.html new file mode 100644 index 000000000..e08c13b9d --- /dev/null +++ b/test/blocks/interactive-metadata/mocks/interactive-metadata.html @@ -0,0 +1,212 @@ +
+
+
+
+

Everyone can. Photoshop.

+

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content + in any image to turn portraits into standout headshots and more.

+

Free Trial Try Generative Fill

+
+
+

+ See it + in action +

+

+ + + + + + +

+
+
+
+ +
+
+
+ +
+
+

Everyone can. Photoshop.

+

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content + in any image to turn portraits into standout headshots and more.

+

Free Trial Try Generative Fill

+
+
+

+ See it + in action +

+

+ + + + + + +

+
+
+
+ +
diff --git a/test/features/interactive-components/crop/crop.test.js b/test/features/interactive-components/crop/crop.test.js new file mode 100644 index 000000000..4570ed272 --- /dev/null +++ b/test/features/interactive-components/crop/crop.test.js @@ -0,0 +1,26 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +const { setLibs } = await import('../../../../creativecloud/scripts/utils.js'); +const { default: init } = await import('../../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); + +describe('interactive metadata', () => { + let im = null; + let ib = null; + + before(async () => { + setLibs('https://milo.adobe.com/libs'); + im = document.querySelector('.interactive-metadata'); + ib = document.querySelector('.marquee'); + await init(im); + }); + it('should render crop layer', () => { + expect(ib.querySelector('.interactive-holder.step-crop')).to.exist; + }); + it('should have start over layer', async () => { + document.querySelector('.crop-button').dispatchEvent(new Event('click')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(ib.querySelector('.interactive-holder.step-start-over')).to.exist; + }); +}); diff --git a/test/features/interactive-components/crop/mocks/body.html b/test/features/interactive-components/crop/mocks/body.html new file mode 100644 index 000000000..2ee974338 --- /dev/null +++ b/test/features/interactive-components/crop/mocks/body.html @@ -0,0 +1,50 @@ +
+
+ +
+
+

Everyone can. Photoshop.

+

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content + in any image to turn portraits into standout headshots and more.

+

Free Trial Try Generative Fill

+
+
+

+ See it + in action +

+

+ + + + + + +

+
+
+
+ +
diff --git a/test/features/interactive-components/generate/generate.test.js b/test/features/interactive-components/generate/generate.test.js new file mode 100644 index 000000000..52845e7ed --- /dev/null +++ b/test/features/interactive-components/generate/generate.test.js @@ -0,0 +1,25 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +const { setLibs } = await import('../../../../creativecloud/scripts/utils.js'); +const { default: init } = await import('../../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); + +describe('interactive metadata', () => { + let im = null; + let ib = null; + + before(async () => { + setLibs('https://milo.adobe.com/libs'); + im = document.querySelector('.interactive-metadata'); + ib = document.querySelector('.marquee'); + await init(im); + }); + it('should render generate layer', () => { + expect(ib.querySelector('.interactive-holder.step-generate')).to.exist; + }); + it('should have start over layer', () => { + document.querySelector('.generate-button').dispatchEvent(new Event('click')); + setTimeout(() => expect(ib).to.equal(''), 200); + }); +}); diff --git a/test/features/interactive-components/generate/mocks/body.html b/test/features/interactive-components/generate/mocks/body.html new file mode 100644 index 000000000..fc22e186a --- /dev/null +++ b/test/features/interactive-components/generate/mocks/body.html @@ -0,0 +1,37 @@ +
+
+ +
+
+

Everyone can. Photoshop.

+

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content in any image to turn portraits into standout headshots and more.

+

Free Trial Try Generative Fill

+
+
+

See it in action

+

+ + + + + + +

+
+
+
+ +
diff --git a/test/features/interactive-components/selector-tray/mocks/body.html b/test/features/interactive-components/selector-tray/mocks/body.html new file mode 100644 index 000000000..5a2341507 --- /dev/null +++ b/test/features/interactive-components/selector-tray/mocks/body.html @@ -0,0 +1,213 @@ +
+
+
+
+

Everyone can. Photoshop.

+

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content + in any image to turn portraits into standout headshots and more.

+

Free Trial Try Generative Fill

+
+
+

+ See it + in action +

+

+ + + + + + +

+
+
+
+ +
diff --git a/test/features/interactive-components/selector-tray/selector-tray.test.js b/test/features/interactive-components/selector-tray/selector-tray.test.js new file mode 100644 index 000000000..70ea0b3ca --- /dev/null +++ b/test/features/interactive-components/selector-tray/selector-tray.test.js @@ -0,0 +1,41 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +const { setLibs } = await import('../../../../creativecloud/scripts/utils.js'); +const { default: init } = await import('../../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); + +describe('Selector tray', () => { + let im = null; + let ib = null; + + before(async () => { + setLibs('https://milo.adobe.com/libs'); + im = document.querySelector('.interactive-metadata'); + ib = document.querySelector('.marquee'); + await init(im); + }); + it('should render selector tray', () => { + expect(ib.querySelector('.interactive-holder.step-selector-tray')).to.exist; + }); + it('should remove selection on hover', async () => { + expect(document.querySelector('.step-selector-tray .thumbnail-selected')).to.exist; + document.querySelector('.tray-thumbnail-img').dispatchEvent(new Event('mouseover')); + expect(document.querySelector('.step-selector-tray .thumbnail-selected')).to.not.exist; + }); + it('should have next layer on selection', async () => { + document.querySelector('.tray-thumbnail-img').dispatchEvent(new Event('click')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(document.querySelector('.interactive-holder.step-crop')).to.exist; + }); + it('should have next layer on with selected thumbnail', async () => { + document.querySelector('.crop-button').dispatchEvent(new Event('click')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(document.querySelector('.step-selector-tray .thumbnail-selected')).to.exist; + }); + it('should have not have thumbnail on selection', async () => { + const thumbnail = document.querySelector('.show-layer .tray-thumbnail-img.thumbnail-selected'); + thumbnail.dispatchEvent(new Event('touchstart')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + }); +}); diff --git a/test/features/interactive-components/slider-tray/mocks/body.html b/test/features/interactive-components/slider-tray/mocks/body.html new file mode 100644 index 000000000..04bc6ca07 --- /dev/null +++ b/test/features/interactive-components/slider-tray/mocks/body.html @@ -0,0 +1,73 @@ +
+
+
+
+
+

Marquee Large Standard

+

Body XL Regular (22/33) Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.

+

Action Action

+

Body XL (22/33) CTA Supplemental text

+
+
+

See it in action

+

+ + + + + + +

+
+
+
+ +
+
+
+
+
+

Marquee Large Standard

+

Body XL Regular (22/33) Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt.

+

Action Action

+

Body XL (22/33) CTA Supplemental text

+
+
+

See it in action

+

+ + + + + + +

+
+
+
+ +
+
diff --git a/test/features/interactive-components/slider-tray/slider-tray.test.js b/test/features/interactive-components/slider-tray/slider-tray.test.js new file mode 100644 index 000000000..22cbabd7b --- /dev/null +++ b/test/features/interactive-components/slider-tray/slider-tray.test.js @@ -0,0 +1,71 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; +import { setLibs } from '../../../../creativecloud/scripts/utils.js'; + +setLibs('/libs'); + +const { default: init } = await import('../../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +window.lana = { log: (msg) => { console.log(msg); } }; +function delay(ms) { + return new Promise((res) => { setTimeout(() => { res(); }, ms); }); +} + +describe('hue-sat-marquee', () => { + let ib = null; + let im = null; + let ibAnimate = null; + let imAnimate = null; + + before(async () => { + ib = document.querySelector('.marquee'); + im = document.querySelector('.interactive-metadata'); + ibAnimate = document.querySelector('.test-animation.marquee'); + imAnimate = document.querySelector('.test-animation.interactive-metadata'); + await init(im); + await init(imAnimate); + await delay(900); + }); + + it('interactive marquee should exist', () => { + const promptbar = document.querySelector('.interactive-enabled'); + expect(promptbar).to.exist; + }); + + it('Stopping animation', () => { + ib.querySelector('.outerCircle').dispatchEvent(new Event('mousedown', { bubbles: true })); + ib.querySelector('.outerCircle').dispatchEvent(new Event('click')); + expect(ib.querySelector('.sliderTray .animate')).to.not.exist; + }); + + it('Set Saturation', () => { + const saturationSlider = ib.querySelector('.saturation-input'); + saturationSlider.value = 180; + saturationSlider.dispatchEvent(new Event('input')); + saturationSlider.value = 180; + saturationSlider.dispatchEvent(new Event('change')); + }); + + it('Tabbing on slider', () => { + document.dispatchEvent(new Event('keydown')); + document.querySelector('.hue-input').dispatchEvent(new Event('focus')); + const focusableEle = document.querySelector('.focusUploadButton'); + expect(focusableEle).to.exist; + }); + + it('Testing upload', async () => { + const uploadBtn = ib.querySelector('.uploadButton'); + uploadBtn.dispatchEvent(new Event('cancel')); + const file = new File([''], 'media_.png', { lastModified: new Date(0), type: 'image/png' }); + uploadBtn.files = [file]; + uploadBtn.dispatchEvent(new Event('change')); + }); + + it('Running animation', async () => { + const { x, y } = ibAnimate.querySelector('.sliderTray').getBoundingClientRect(); + window.scrollTo(x, y); + await delay(900); + ibAnimate.querySelector('.outerCircle').dispatchEvent(new Event('transitionend')); + await delay(600); + }); +}); diff --git a/test/features/interactive-components/start-over/mocks/body.html b/test/features/interactive-components/start-over/mocks/body.html new file mode 100644 index 000000000..da64587d2 --- /dev/null +++ b/test/features/interactive-components/start-over/mocks/body.html @@ -0,0 +1,36 @@ +
+
+
+
+

Everyone can. Photoshop.

+

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content in any image to turn portraits into standout headshots and more.

+

Free Trial Try Generative Fill

+
+
+

See it in action

+

+ + + + + + +

+
+
+
+ +
diff --git a/test/features/interactive-components/start-over/start-over.test.js b/test/features/interactive-components/start-over/start-over.test.js new file mode 100644 index 000000000..d59f651c8 --- /dev/null +++ b/test/features/interactive-components/start-over/start-over.test.js @@ -0,0 +1,34 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +const { setLibs } = await import('../../../../creativecloud/scripts/utils.js'); +const { default: init } = await import('../../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); + +describe('Start Over', () => { + let im = null; + let ib = null; + + before(async () => { + setLibs('https://milo.adobe.com/libs'); + im = document.querySelector('.interactive-metadata'); + ib = document.querySelector('.marquee'); + await init(im); + }); + it('should render generate layer', () => { + expect(ib.querySelector('.interactive-holder.step-generate')).to.exist; + }); + it('should have start over layer', async () => { + document.querySelector('.generate-button').dispatchEvent(new Event('click')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(document.querySelector('.interactive-holder.step-start-over')).to.exist; + }); + it('should have start over button', () => { + expect(document.querySelector('.start-over-button')).to.exist; + }); + it('should have generate layer', async () => { + document.querySelector('.start-over-button').dispatchEvent(new Event('click')); + await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + expect(document.querySelector('.interactive-holder.step-generate')).to.exist; + }); +}); From 79a1623b17501e207d90a5c5227bd9bda4f17ba7 Mon Sep 17 00:00:00 2001 From: Aishwarya Mathuria Date: Mon, 8 Apr 2024 20:18:34 +0530 Subject: [PATCH 04/10] MWPW-146221 | Improving performance for hue sat (#261) * Improving performance for hue sat Resolves: MWPW-146221 Test URLs: Before: https://main--cc--adobecom.hlx.live/drafts/mathuria/interactiveelems/demo/huesat/hue-sat-marquee1?martech=off After: https://mwpw-146221--cc--aishwaryamathuria.hlx.live/drafts/mathuria/interactiveelems/demo/huesat/hue-sat-marquee1?martech=off --- .../blocks/interactive-metadata/interactive-metadata.js | 1 + 1 file changed, 1 insertion(+) diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.js b/creativecloud/blocks/interactive-metadata/interactive-metadata.js index 8fd7e47b4..e6da45089 100644 --- a/creativecloud/blocks/interactive-metadata/interactive-metadata.js +++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.js @@ -166,6 +166,7 @@ async function implementWorkflow(stepInfo) { if (stepInfo.stepIndex === 0) await addLayerAnimation(stepInfo.target); } await handleLayerDisplay(stepInfo); + if (stepInfo.stepList.length === 1) return; await handleNextStep(stepInfo); } From 58a5a8bf10e24aa4f06f546ac2c54b1a12610bc8 Mon Sep 17 00:00:00 2001 From: Aishwarya Mathuria Date: Wed, 10 Apr 2024 22:47:33 +0530 Subject: [PATCH 05/10] MWPW-146229 | Integrating review comments and new workflows (#264) Change heading-top to mobile-heading-top Change generate-right to generate-side Change the bullet list in selector tray to numbered list Change the ordering of the list content with thumbnail first and display image next. Adding new workflows workflow-generate-select-generate Generate -> Selector Tray -> Generate -> Crop -> Start Over workflow-generate-selector Generate -> Selector Tray -> Generate -> Start over workflow-generate-triple-selector Generate -> Selector Tray -> Generate -> Selector Tray -> Generate -> Selector Tray -> Start over --- .../interactive-metadata.css | 31 ++-- .../interactive-metadata.js | 9 +- .../interactive-components/crop/crop.js | 4 +- .../generate/generate.css | 20 +-- .../generate/generate.js | 4 +- .../selector-tray/selector-tray.js | 32 ++-- .../slider-tray/slider-tray.js | 2 +- .../start-over/start-over.css | 2 +- .../interactive-metadata.test.js | 72 ++++----- .../mocks/interactive-metadata.html | 147 +----------------- .../crop/mocks/body.html | 2 +- .../generate/mocks/body.html | 2 +- .../selector-tray/mocks/body.html | 2 +- .../start-over/mocks/body.html | 2 +- 14 files changed, 90 insertions(+), 241 deletions(-) diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.css b/creativecloud/blocks/interactive-metadata/interactive-metadata.css index f01cb4a94..806979cb8 100644 --- a/creativecloud/blocks/interactive-metadata/interactive-metadata.css +++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.css @@ -17,16 +17,6 @@ margin: 0; } -.interactive-enabled.heading-top h1, -.interactive-enabled.heading-top h2, -.interactive-enabled.heading-top h3, -.interactive-enabled.heading-top h4, -.interactive-enabled.heading-top h5, -.interactive-enabled.heading-top h6, -.interactive-enabled .foreground .image .mobile-top-title { - display: none; -} - .interactive-enabled .foreground.container .image { flex-direction: column; } @@ -269,7 +259,17 @@ width: 100%; } - .interactive-enabled.heading-top .foreground .image .mobile-top-title { + .interactive-enabled.mobile-heading-top h1, + .interactive-enabled.mobile-heading-top h2, + .interactive-enabled.mobile-heading-top h3, + .interactive-enabled.mobile-heading-top h4, + .interactive-enabled.mobile-heading-top h5, + .interactive-enabled.mobile-heading-top h6, + .interactive-enabled .foreground .image .mobile-heading-top { + display: none; + } + + .interactive-enabled.mobile-heading-top .foreground .image .mobile-heading-top { display: block; margin-bottom: 24px; font-size: 36px; @@ -279,13 +279,8 @@ } @media screen and (min-width: 600px) { - .interactive-enabled.heading-top h1, - .interactive-enabled.heading-top h2, - .interactive-enabled.heading-top h3, - .interactive-enabled.heading-top h4, - .interactive-enabled.heading-top h5, - .interactive-enabled.heading-top h6 { - display: block; + .interactive-enabled .foreground .image .mobile-heading-top { + display: none; } .interactive-enabled .foreground.container .image > p:first-child { diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.js b/creativecloud/blocks/interactive-metadata/interactive-metadata.js index e6da45089..136097446 100644 --- a/creativecloud/blocks/interactive-metadata/interactive-metadata.js +++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.js @@ -172,7 +172,7 @@ async function implementWorkflow(stepInfo) { function checkRenderStatus(targetBlock, res, rej, etime, rtime) { if (etime > 20000) { rej(); return; } - if (targetBlock.querySelector('.text') && targetBlock.querySelector('.image')) res(); + if (targetBlock.querySelector('.text') && targetBlock.querySelector('.asset, .image')) res(); else setTimeout(() => checkRenderStatus(targetBlock, res, rej, etime + rtime), rtime); } @@ -196,11 +196,11 @@ function decorateEnticementArrow(aa) { } function decorateMobileHeading(intEnb) { - if (!intEnb.classList.contains('heading-top')) return; + if (!intEnb.classList.contains('mobile-heading-top')) return; const h = intEnb.querySelector('.text').querySelector('h1, h2, h3, h4, h5, h6'); if (!h) return; const htxt = h.textContent; - const hTxtTop = createTag('div', { class: 'mobile-top-title' }, htxt); + const hTxtTop = createTag('div', { class: 'mobile-heading-top' }, htxt); intEnb.querySelector('.image').prepend(hTxtTop); } @@ -256,6 +256,9 @@ function getWorkFlowInformation(el) { 'workflow-generate-crop': ['generate', 'selector-tray', 'crop', 'start-over'], 'workflow-generate-repeat-crop': ['generate', 'selector-tray', 'generate', 'selector-tray', 'crop', 'start-over'], 'workflow-hue-sat': ['slider-tray'], + 'workflow-generate-select-generate': ['generate', 'selector-tray', 'generate', 'crop', 'start-over'], + 'workflow-generate-selector': ['generate', 'selector-tray', 'generate', 'start-over'], + 'workflow-generate-triple-selector': ['generate', 'selector-tray', 'generate', 'selector-tray', 'generate', 'selector-tray', 'start-over'], }; const wfNames = Object.keys(intWorkFlowConfig); [...el.classList].forEach((cn) => { if (cn.match('workflow-')) wfName = cn; }); diff --git a/creativecloud/features/interactive-components/crop/crop.js b/creativecloud/features/interactive-components/crop/crop.js index 50aa1b2da..0675a800f 100644 --- a/creativecloud/features/interactive-components/crop/crop.js +++ b/creativecloud/features/interactive-components/crop/crop.js @@ -6,11 +6,13 @@ export default async function stepInit(data) { const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); const cropCTA = createTag('a', { class: 'gray-button body-m crop-button', href: '#' }); const svg = config.querySelector('img[src*=".svg"')?.closest('picture'); + const lastp = config.querySelector(':scope > div > p:last-child'); + const textContent = lastp.textContent.trim(); if (svg) { svg.insertAdjacentElement('afterend', svg.cloneNode(true)); cropCTA.appendChild(createTag('div', { class: 'crop-icon-container' }, svg)); } - if (config.textContent) cropCTA.appendChild(document.createTextNode(config.textContent.trim())); + if (textContent) cropCTA.appendChild(document.createTextNode(textContent)); cropCTA.addEventListener('click', async (e) => { e.preventDefault(); await data.openForExecution; diff --git a/creativecloud/features/interactive-components/generate/generate.css b/creativecloud/features/interactive-components/generate/generate.css index 1409406d5..64bb14465 100644 --- a/creativecloud/features/interactive-components/generate/generate.css +++ b/creativecloud/features/interactive-components/generate/generate.css @@ -84,15 +84,15 @@ padding: 0; } - .interactive-enabled.generate-right .step-generate .generate-prompt-button, - .interactive-enabled .step-generate .generate-right .generate-prompt-button { + .interactive-enabled.generate-side .step-generate .generate-prompt-button, + .interactive-enabled .step-generate .generate-side .generate-prompt-button { left: auto; right: -21.5%; bottom: calc(50% - 80px); } - [dir="rtl"] .interactive-enabled.generate-right .step-generate .generate-prompt-button, - [dir="rtl"] .interactive-enabled .step-generate .generate-right .generate-prompt-button { + [dir="rtl"] .interactive-enabled.generate-side .step-generate .generate-prompt-button, + [dir="rtl"] .interactive-enabled .step-generate .generate-side .generate-prompt-button { left: auto; right: -21.5%; } @@ -131,21 +131,21 @@ max-width: 218px; } - .interactive-enabled.row-reversed.generate-right .step-generate .generate-prompt-button, - .interactive-enabled.row-reversed .step-generate .generate-right .generate-prompt-button { + .interactive-enabled.row-reversed.generate-side .step-generate .generate-prompt-button, + .interactive-enabled.row-reversed .step-generate .generate-side .generate-prompt-button { flex-direction: row; left: -21.5%; right: auto; } - [dir="rtl"] .interactive-enabled.generate-right .step-generate .generate-prompt-button, - [dir="rtl"] .interactive-enabled .step-generate .generate-right .generate-prompt-button { + [dir="rtl"] .interactive-enabled.generate-side .step-generate .generate-prompt-button, + [dir="rtl"] .interactive-enabled .step-generate .generate-side .generate-prompt-button { left: -21.5%; right: auto; } - [dir="rtl"] .interactive-enabled.row-reversed.generate-right .step-generate .generate-prompt-button, - [dir="rtl"] .interactive-enabled.row-reversed .step-generate .generate-right .generate-prompt-button { + [dir="rtl"] .interactive-enabled.row-reversed.generate-side .step-generate .generate-prompt-button, + [dir="rtl"] .interactive-enabled.row-reversed .step-generate .generate-side .generate-prompt-button { left: auto; right: -21.5%; } diff --git a/creativecloud/features/interactive-components/generate/generate.js b/creativecloud/features/interactive-components/generate/generate.js index 0e11aeeef..3421b287b 100644 --- a/creativecloud/features/interactive-components/generate/generate.js +++ b/creativecloud/features/interactive-components/generate/generate.js @@ -4,8 +4,8 @@ export default async function stepInit(data) { data.target.classList.add('step-generate'); const config = data.stepConfigs[data.stepIndex]; const layer = createTag('div', { class: `layer layer-${data.stepIndex}` }); - const [searchText, btnText, position] = config.textContent.trim().split('|'); - if (position) layer.classList.add(`generate-${position.toLowerCase().trim()}`); + const lastp = config.querySelector(':scope > div > p:last-child'); + const [searchText, btnText] = lastp.textContent.trim().split('|'); const genfillDiv = createTag('div', { class: 'generate-prompt-button body-m' }); const searchBar = createTag('div', { class: 'generate-text' }, `${searchText}`); const searchBarContainer = createTag('div', { class: 'generate-text-container' }, searchBar); diff --git a/creativecloud/features/interactive-components/selector-tray/selector-tray.js b/creativecloud/features/interactive-components/selector-tray/selector-tray.js index c138d524a..2648e844d 100644 --- a/creativecloud/features/interactive-components/selector-tray/selector-tray.js +++ b/creativecloud/features/interactive-components/selector-tray/selector-tray.js @@ -3,17 +3,17 @@ import { handleImageTransition, getImgSrc } from '../../../blocks/interactive-me function getTrayConfig(data) { const dpth = data.displayPath; - const allUls = data.stepConfigs[data.stepIndex].querySelectorAll('ul'); - const configUl = (dpth >= 0 && allUls.length > dpth) ? allUls[dpth] : allUls[0]; - return configUl; + const allTrays = data.stepConfigs[data.stepIndex].querySelectorAll('ul, ol'); + const configTray = (dpth >= 0 && allTrays.length > dpth) ? allTrays[dpth] : allTrays[0]; + return configTray; } function getStartingPathIdx(data) { let pathIdx = 0; const dpth = data.displayPath; - const allUls = data.stepConfigs[data.stepIndex].querySelectorAll('ul'); - if (allUls.length < dpth) return pathIdx; - for (let i = 0; i < dpth; i += 1) pathIdx += allUls[i].querySelectorAll('li').length; + const allTrays = data.stepConfigs[data.stepIndex].querySelectorAll('ul, ol'); + if (allTrays.length < dpth) return pathIdx; + for (let i = 0; i < dpth; i += 1) pathIdx += allTrays[i].querySelectorAll('li').length; return pathIdx; } @@ -47,23 +47,20 @@ function attachThumbnailEvents(a, data, layer) { const trObj = { src: curra.dataset.dispSrc, alt: curra.dataset.dispAlt, useCfg: true }; await handleImageTransition(data, trObj); data.el.dispatchEvent(new CustomEvent(data.nextStepEvent)); - e.target.closest('.tray-items').querySelector('a.tray-thumbnail-img').classList.add('thumbnail-selected'); }); } function selectorTrayWithImgs(layer, data) { const selectorTray = createTag('div', { class: 'body-s selector-tray' }); const trayItems = createTag('div', { class: 'tray-items' }); - const configUl = getTrayConfig(data); - const pics = configUl.querySelectorAll('picture'); + const configTray = getTrayConfig(data); + const options = configTray.querySelectorAll('li'); let pathIdx = getStartingPathIdx(data); let displayImg = null; - [...pics].forEach((pic, idx) => { - if (idx % 2 === 0) { - displayImg = [getImgSrc(pic), pic.querySelector('img').alt]; - return; - } - const a = createSelectorThumbnail(pic, pathIdx, displayImg); + [...options].forEach((o) => { + const [thumbnailPic, displayPic] = o.querySelectorAll('picture'); + displayImg = [getImgSrc(displayPic), displayPic.querySelector('img').alt]; + const a = createSelectorThumbnail(thumbnailPic, pathIdx, displayImg); trayItems.append(a); pathIdx += 1; attachThumbnailEvents(a, data, layer); @@ -79,10 +76,7 @@ export default async function stepInit(data) { const title = config.querySelector('p:first-child'); let trayTitle = null; if (title) trayTitle = createTag('div', { class: 'tray-title' }, title.innerText.trim()); - const trayConfig = config.querySelectorAll('ul > li'); - const isGenerateTray = [...trayConfig].filter((li) => (li.querySelector('img[src*="media_"]').length >= 2)); - let selectorTray = null; - if (isGenerateTray) selectorTray = selectorTrayWithImgs(layer, data); + const selectorTray = selectorTrayWithImgs(layer, data); if (title) selectorTray.prepend(trayTitle); layer.append(selectorTray); return layer; diff --git a/creativecloud/features/interactive-components/slider-tray/slider-tray.js b/creativecloud/features/interactive-components/slider-tray/slider-tray.js index 57769363b..cbb1e8ec1 100644 --- a/creativecloud/features/interactive-components/slider-tray/slider-tray.js +++ b/creativecloud/features/interactive-components/slider-tray/slider-tray.js @@ -15,7 +15,7 @@ async function createSelectorTray(data, layer) { const sliderTray = createTag('div', { class: 'sliderTray' }); const menu = createTag('div', { class: 'menu' }); const config = data.stepConfigs[data.stepIndex]; - const options = config.querySelectorAll(':scope > div .icon'); + const options = config.querySelectorAll(':scope > div ul .icon, :scope > div ol .icon'); [...options].forEach((o) => { handleInput(o, sliderTray, menu, layer); }); layer.append(sliderTray); observeSliderTray(sliderTray, data.target, menu); diff --git a/creativecloud/features/interactive-components/start-over/start-over.css b/creativecloud/features/interactive-components/start-over/start-over.css index 21e03c97e..ee87bb970 100644 --- a/creativecloud/features/interactive-components/start-over/start-over.css +++ b/creativecloud/features/interactive-components/start-over/start-over.css @@ -14,7 +14,7 @@ gap: 16px; } - .interactive-enabled.generate-right .step-start-over .layer .gray-button.start-over-button { + .interactive-enabled.generate-side .step-start-over .layer .gray-button.start-over-button { bottom: 32px; } } diff --git a/test/blocks/interactive-metadata/interactive-metadata.test.js b/test/blocks/interactive-metadata/interactive-metadata.test.js index c281e889f..b64fdcca8 100644 --- a/test/blocks/interactive-metadata/interactive-metadata.test.js +++ b/test/blocks/interactive-metadata/interactive-metadata.test.js @@ -1,32 +1,37 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; -document.body.innerHTML = await readFile({ path: './mocks/interactive-metadata.html' }); const { setLibs } = await import('../../../creativecloud/scripts/utils.js'); const { default: init } = await import('../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); +const { handleImageTransition } = await import('../../../creativecloud/blocks/interactive-metadata/interactive-metadata.js'); +function delay(ms) { + return new Promise((res) => { setTimeout(() => { res(); }, ms); }); +} +document.body.innerHTML = await readFile({ path: './mocks/interactive-metadata.html' }); describe('interactive metadata', () => { let im = null; let ib = null; - let genfillIm = null; - let genfillMarquee = null; before(async () => { setLibs('https://milo.adobe.com/libs'); im = document.querySelector('.interactive-metadata'); ib = document.querySelector('.marquee'); - genfillIm = document.querySelector('.interactive-metadata.workflow-genfill'); - genfillMarquee = genfillIm.closest('.section').querySelector('.marquee'); await init(im); - await init(genfillIm); - }); - it('interactive metadata should exist', () => { - expect(im).to.exist; }); + it('should make the previous block interactive-enabled', () => { expect(ib).to.exist; expect(ib.classList.contains('interactive-enabled')).to.be.true; }); + + it('should start animation', async () => { + const { x, y } = ib.querySelector('.gray-button').getBoundingClientRect(); + window.scrollTo(x, y); + await delay(200); + expect(ib.querySelector('.interactive-holder .show-layer .gray-button.animated')).to.exist; + }); + it('should create a workflow', () => { let hasWorkflowClass = false; im.classList.forEach((className) => { @@ -36,37 +41,32 @@ describe('interactive metadata', () => { }); expect(hasWorkflowClass).to.be.true; }); - it('should render next selector tray', async () => { - im.dispatchEvent(new CustomEvent('cc:interactive-switch')); - await new Promise((res) => { setTimeout(() => { res(); }, 200); }); - expect(ib.querySelector('.interactive-holder.step-selector-tray')).to.exist; - }); - it('should render next crop layer', async () => { + + it('should render next layer', async () => { im.dispatchEvent(new CustomEvent('cc:interactive-switch')); - await new Promise((res) => { setTimeout(() => { res(); }, 200); }); - expect(ib.querySelector('.interactive-holder.step-crop')).to.exist; + await delay(200); + expect(ib.querySelector('.interactive-holder .layer-1.show-layer')).to.exist; }); - it('should render next start-over layer', async () => { + + it('should render start over layer', async () => { im.dispatchEvent(new CustomEvent('cc:interactive-switch')); - await new Promise((res) => { setTimeout(() => { res(); }, 200); }); + await delay(200); expect(ib.querySelector('.interactive-holder.step-start-over')).to.exist; }); - it('should render next generate layer', async () => { - im.dispatchEvent(new CustomEvent('cc:interactive-switch')); - await new Promise((res) => { setTimeout(() => { res(); }, 200); }); - expect(ib.querySelector('.interactive-holder.step-generate')).to.exist; - }); - it('Genfill: should render generate layer', () => { - expect(genfillMarquee.querySelector('.interactive-holder.step-generate')).to.exist; - }); - it('Genfill: should render generate layer', async () => { - genfillIm.dispatchEvent(new CustomEvent('cc:interactive-switch')); - await new Promise((res) => { setTimeout(() => { res(); }, 200); }); - expect(genfillMarquee.querySelector('.interactive-holder.step-generate')).to.exist; - }); - it('Genfill: should render start over layer', async () => { - genfillIm.dispatchEvent(new CustomEvent('cc:interactive-switch')); - await new Promise((res) => { setTimeout(() => { res(); }, 200); }); - expect(genfillMarquee.querySelector('.interactive-holder.step-start-over')).to.exist; + + it('Transition video', async () => { + const trgt = ib.querySelector('.image .interactive-holder'); + const mockStepInfo = { + im, + stepIndex: 0, + stepName: 'generate', + stepList: ['generate', 'generate', 'start-over'], + stepConfigs: im.querySelectorAll(':scope > div'), + target: trgt, + displayPath: 0, + openForExecution: Promise.resolve(true), + }; + const transitionCfg = { useCfg: true, vsrc: `${window.origin}/videoo.mp4#_autoplay` }; + await handleImageTransition(mockStepInfo, transitionCfg); }); }); diff --git a/test/blocks/interactive-metadata/mocks/interactive-metadata.html b/test/blocks/interactive-metadata/mocks/interactive-metadata.html index e08c13b9d..038a34eac 100644 --- a/test/blocks/interactive-metadata/mocks/interactive-metadata.html +++ b/test/blocks/interactive-metadata/mocks/interactive-metadata.html @@ -1,150 +1,5 @@
-
-
-
-

Everyone can. Photoshop.

-

Dream it, type it, and see it with generative AI features in Photoshop. Add or extend content - in any image to turn portraits into standout headshots and more.

-

Free Trial Try Generative Fill

-
-
-

- See it - in action -

-

- - - - - - -

-
-
-
- -
-
-
diff --git a/test/features/interactive-components/crop/mocks/body.html b/test/features/interactive-components/crop/mocks/body.html index 2ee974338..1f5807e71 100644 --- a/test/features/interactive-components/crop/mocks/body.html +++ b/test/features/interactive-components/crop/mocks/body.html @@ -1,5 +1,5 @@
-
diff --git a/test/features/interactive-components/generate/mocks/body.html b/test/features/interactive-components/generate/mocks/body.html index fc22e186a..9873daf20 100644 --- a/test/features/interactive-components/generate/mocks/body.html +++ b/test/features/interactive-components/generate/mocks/body.html @@ -1,5 +1,5 @@
-
+
diff --git a/test/features/interactive-components/selector-tray/mocks/body.html b/test/features/interactive-components/selector-tray/mocks/body.html index 5a2341507..813b29aee 100644 --- a/test/features/interactive-components/selector-tray/mocks/body.html +++ b/test/features/interactive-components/selector-tray/mocks/body.html @@ -1,5 +1,5 @@
-
diff --git a/test/features/interactive-components/start-over/mocks/body.html b/test/features/interactive-components/start-over/mocks/body.html index da64587d2..4f301305b 100644 --- a/test/features/interactive-components/start-over/mocks/body.html +++ b/test/features/interactive-components/start-over/mocks/body.html @@ -1,5 +1,5 @@
-
+

Everyone can. Photoshop.

From ea60f3c3a66faa91ce1f3fb15ae10a67d2836c01 Mon Sep 17 00:00:00 2001 From: Arshad Mohammed <87503056+arshadparwaiz@users.noreply.github.com> Date: Thu, 11 Apr 2024 04:13:20 -0700 Subject: [PATCH 06/10] MWPW-142006 - Enable repo-sync for graybox (#263) Enable CC repo-sync for CC graybox repo Resolves: MWPW-142006 --- .github/workflows/graybox-sync-repos.yml | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/graybox-sync-repos.yml diff --git a/.github/workflows/graybox-sync-repos.yml b/.github/workflows/graybox-sync-repos.yml new file mode 100644 index 000000000..58d1f9091 --- /dev/null +++ b/.github/workflows/graybox-sync-repos.yml @@ -0,0 +1,66 @@ +name: Graybox Repo Sync + +on: + workflow_dispatch: + inputs: + syncBranch: + description: 'Branch to sync' + required: true + default: 'stage' + type: choice + options: + - 'stage' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Generate a token + id: generate_token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.FG_SYNC_APP_ID }} + private-key: ${{ secrets.FG_SYNC_APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: "cc-graybox" + + - name: Checkout Repo + uses: actions/checkout@v2 + with: + persist-credentials: false + ref: ${{ inputs.syncBranch }} + + - name: Clone Graybox Repository and Checkout Stage Branch + run: | + git clone https://github.com/adobecom/cc-graybox.git ../cc-graybox + cd ../cc-graybox + git checkout $GB_SYNC_BRANCH + echo "cc-graybox branch" + git branch + cd ../cc + echo "cc branch" + git branch + env: + GB_SYNC_BRANCH: ${{ inputs.syncBranch }} + + - name: Overwrite graybox repo files with latest from source repo + run: | + rsync -av --exclude='fstab.yaml' --exclude='.github' --exclude='.kodiak' --exclude='.git' --exclude='.idea' --exclude='.husky' --exclude='.vscode' --exclude='tools/sidekick/config.json' ./ ../cc-graybox/ + + - name: Commit and Push Changes to Graybox Repository + run: | + cd ../cc-graybox + echo "cc-graybox branch" + git branch + git config user.email "$FG_SYNC_BOT_EMAIL" + git config user.name "milo-repo-sync[bot]" + git status + git remote set-url origin https://oauth2:$GITHUB_TOKEN@github.com/adobecom/cc-graybox.git + git remote -v + git add . + git commit -m "Syncing cc to cc-graybox" + git push origin $GB_SYNC_BRANCH --force + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + FG_SYNC_BOT_EMAIL: ${{ secrets.FG_SYNC_BOT_EMAIL }} + GB_SYNC_BRANCH: ${{ inputs.syncBranch }} From 010db8c9ee279687a748fb4453aa45d014d36dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ilyas=20St=C3=A9phane=20T=C3=BCrkben?= Date: Thu, 11 Apr 2024 13:13:41 +0200 Subject: [PATCH 07/10] MWPW-143951: Optimize sidenav (#246) move merch-card-collection and sidenav inside catalog block. includes: https://git.corp.adobe.com/wcms/tacocat.js/pull/552/files depends on: adobecom/milo#2057 Resolves: MWPW-143951 Test URLs: Before: https://main--cc--adobecom.hlx.live/drafts/ilyas/catalog-sidenav?martech=off&georouting=off After: https://mwpw-143951--cc--yesil.hlx.live/drafts/ilyas/catalog?martech=off&georouting=off --- creativecloud/blocks/catalog/catalog.css | 4 +- creativecloud/blocks/catalog/catalog.js | 50 ++++++++++++++---------- creativecloud/blocks/sidenav/sidenav.js | 43 +++++++++++--------- creativecloud/deps/merch-sidenav.js | 50 +++++++++++++++++------- creativecloud/scripts/scripts.js | 1 - 5 files changed, 91 insertions(+), 57 deletions(-) diff --git a/creativecloud/blocks/catalog/catalog.css b/creativecloud/blocks/catalog/catalog.css index 035066f23..44a7e7f07 100644 --- a/creativecloud/blocks/catalog/catalog.css +++ b/creativecloud/blocks/catalog/catalog.css @@ -18,12 +18,12 @@ } } -.catalog > merch-sidenav { +.catalog.app > merch-sidenav { grid-column: 1 / span 1; order: 0; } -.catalog > merch-cards { +.catalog.app > merch-card-collection { align-self: baseline; grid-column: 2 / span 1; order: 1; diff --git a/creativecloud/blocks/catalog/catalog.js b/creativecloud/blocks/catalog/catalog.js index 872ea3cad..453ed48d5 100644 --- a/creativecloud/blocks/catalog/catalog.js +++ b/creativecloud/blocks/catalog/catalog.js @@ -1,26 +1,36 @@ -/* eslint-disable chai-friendly/no-unused-expressions */ import { getLibs } from '../../scripts/utils.js'; +const miloLibs = getLibs('/libs'); +const { loadStyle } = await import(`${miloLibs}/utils/utils.js`); + +// Helps with TBT: MWPW-145127 +loadStyle(`${miloLibs}/blocks/global-navigation/features/profile/dropdown.css`); + +/** container block */ export default async function init(el) { - const miloLibs = getLibs(); - import(`${miloLibs}/deps/lit-all.min.js`); - import(`${miloLibs}/blocks/merch-cards/merch-cards.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/theme.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/button.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/icons/checkmark.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/icons/chevron.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/help-text.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/icon.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/icons-ui.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/icons-workflow.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/menu.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/overlay.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/popover.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/reactive-controllers.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/search.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/shared.js`); - import(`${miloLibs}/features/spectrum-web-components/dist/textfield.js`); - el.classList.add('merch', 'app'); + el.classList.add('app'); + const libs = getLibs(); + const sidenavEl = el.querySelector('.sidenav'); + const merchCardsEl = el.querySelector('.merch-card-collection'); el.innerHTML = ''; + let merchCards; + if (merchCardsEl) { + el.appendChild(merchCardsEl); + merchCardsEl.classList.add('four-merch-cards'); + const { default: initMerchCards } = await import(`${libs}/blocks/merch-card-collection/merch-card-collection.js`); + merchCards = await initMerchCards(merchCardsEl); + } + if (sidenavEl) { + (merchCards?.updateComplete ?? Promise.resolve()).then(async () => { + const { default: initSidenav } = await import('../sidenav/sidenav.js'); + const sidenav = await initSidenav(sidenavEl); + el.appendChild(sidenav); + await sidenav.updateComplete; + if (merchCards) { + merchCards.sidenav = sidenav; + merchCards.requestUpdate(); + } + }); + } return el; } diff --git a/creativecloud/blocks/sidenav/sidenav.js b/creativecloud/blocks/sidenav/sidenav.js index 43d07537a..8135ab9a7 100644 --- a/creativecloud/blocks/sidenav/sidenav.js +++ b/creativecloud/blocks/sidenav/sidenav.js @@ -1,10 +1,12 @@ import { createTag, localizeLink, getLibs } from '../../scripts/utils.js'; -import '../../deps/merch-sidenav.js'; - const CATEGORY_ID_PREFIX = 'categories/'; const TYPE_ID_PREFIX = 'types/'; +// allows improve TBT by returning control to the main thread. +// eslint-disable-next-line no-promise-executor-return +const makePause = async (timeout = 0) => new Promise((resolve) => setTimeout(resolve, timeout)); + const getIdLeaf = (id) => (id?.substring(id.lastIndexOf('/') + 1) || id).toLowerCase(); const getCategories = (items, isMultilevel, mapCategories) => { @@ -83,6 +85,7 @@ const appendFilters = async (root, link, explicitCategoriesElt, typeText) => { } let shallowCategories = true; if (categoryValues.length > 0) { + await makePause(); const items = categoryValues.map((value) => mapCategories[value]); const parentValues = new Set(items.map((value) => value?.id.split('/')[1])); // all parent will always be here without children, @@ -92,6 +95,7 @@ const appendFilters = async (root, link, explicitCategoriesElt, typeText) => { root.append(categoryTags); } if (typeText && types.length > 0) { + await makePause(); root.append(getTypes(types, typeText)); } } @@ -128,43 +132,44 @@ function appendResources(rootNav, resourceLink) { export default async function init(el) { const libs = getLibs(); - await Promise.all([ + const merchSidenavDep = import('../../deps/merch-sidenav.js'); + const deps = Promise.all([ + merchSidenavDep, + // eslint-disable-next-line import/no-unresolved, import/no-absolute-path + import('/libs/deps/lit-all.min.js'), import(`${libs}/features/spectrum-web-components/dist/theme.js`), + import(`${libs}/features/spectrum-web-components/dist/base.js`), + import(`${libs}/features/spectrum-web-components/dist/shared.js`), import(`${libs}/features/spectrum-web-components/dist/sidenav.js`), import(`${libs}/features/spectrum-web-components/dist/search.js`), import(`${libs}/features/spectrum-web-components/dist/checkbox.js`), + import(`${libs}/features/spectrum-web-components/dist/button.js`), import(`${libs}/features/spectrum-web-components/dist/dialog.js`), + import(`${libs}/features/spectrum-web-components/dist/overlay.js`), ]); - const title = el.querySelector('h2')?.textContent.trim(); + const title = el.querySelector('h2,h3')?.textContent.trim(); + await merchSidenavDep; const rootNav = createTag('merch-sidenav', { title }); const searchText = el.querySelector('p > strong')?.textContent.trim(); const typeText = el.querySelector('p > em')?.textContent.trim(); + await deps; + el.replaceWith(rootNav); appendSearch(rootNav, searchText); // eslint-disable-next-line prefer-const let [endpoint, resourcesLink] = el.querySelectorAll('a'); if (endpoint) { + await makePause(); endpoint = localizeLink(endpoint.textContent.trim(), null, true); const explicitCategories = el.querySelector('ul'); + performance.mark('sidenav:appendFilters:start'); await appendFilters(rootNav, endpoint, explicitCategories, typeText); + performance.mark('sidenav:appendFilters:end'); + performance.measure('sidenav:appendFilters', 'sidenav:appendFilters:start', 'sidenav:appendFilters:end'); } if (resourcesLink) { + await makePause(); appendResources(rootNav, resourcesLink); } - - const appContainer = document.querySelector('.merch.app'); - if (appContainer) { - appContainer.appendChild(rootNav); - rootNav.updateComplete.then(() => { - el.remove(); - const merchCards = appContainer.querySelector('merch-cards'); - if (merchCards) { - merchCards.sidenav = merchCards.sidenav || rootNav; - merchCards.requestUpdate(); - } - }); - } else { - el.replaceWith(rootNav); - } return rootNav; } diff --git a/creativecloud/deps/merch-sidenav.js b/creativecloud/deps/merch-sidenav.js index 6f42b09bd..7400c1d07 100644 --- a/creativecloud/deps/merch-sidenav.js +++ b/creativecloud/deps/merch-sidenav.js @@ -1,4 +1,4 @@ -// Wed, 20 Dec 2023 12:32:36 GMT +// Thu, 21 Mar 2024 10:55:19 GMT // src/sidenav/merch-sidenav.js import { html as html4, css as css5, LitElement as LitElement4 } from "/libs/deps/lit-all.min.js"; @@ -348,12 +348,18 @@ var SPECTRUM_MOBILE_LANDSCAPE = "(max-width: 700px)"; var TABLET_DOWN = "(max-width: 1200px)"; // src/sidenav/merch-sidenav.js -var EVENT_OPEN_MODAL = "merch-sidenav:open-modal"; var MerchSideNav = class extends LitElement4 { static properties = { title: { type: String }, - closeText: { type: String, attribute: "close-text" } + closeText: { type: String, attribute: "close-text" }, + modal: { type: Boolean, attribute: "modal", reflect: true } }; + // modal target + #target; + constructor() { + super(); + this.modal = false; + } static styles = [ css5` :host { @@ -380,6 +386,8 @@ var MerchSideNav = class extends LitElement4 { return this.mobileAndTablet.matches ? this.asDialog : this.asAside; } get asDialog() { + if (!this.modal) + return; return html4` `; } - async showModal({ target }) { - this.dispatchEvent(new Event(EVENT_OPEN_MODAL)); - const content = this.shadowRoot.querySelector("sp-dialog-wrapper"); - const options = { - trigger: target, - type: "modal" - }; - const overlay = await window.__merch__spectrum_Overlay.open( - content, - options - ); - this.shadowRoot.querySelector("sp-theme").append(overlay); + openModal() { + this.updateComplete.then(async () => { + const content = this.shadowRoot.querySelector("sp-dialog-wrapper"); + const options = { + trigger: this.#target, + type: "modal" + }; + const overlay = await window.__merch__spectrum_Overlay.open( + content, + options + ); + overlay.addEventListener("close", () => { + this.modal = false; + }); + this.shadowRoot.querySelector("sp-theme").append(overlay); + }); + } + updated() { + if (this.modal) + this.openModal(); + } + showModal({ target }) { + this.#target = target; + this.modal = true; } }; customElements.define("merch-sidenav", MerchSideNav); diff --git a/creativecloud/scripts/scripts.js b/creativecloud/scripts/scripts.js index 00f74754f..cd8b45ce1 100644 --- a/creativecloud/scripts/scripts.js +++ b/creativecloud/scripts/scripts.js @@ -128,7 +128,6 @@ const CONFIG = { locales, geoRouting: 'on', prodDomains: ['www.adobe.com', 'helpx.adobe.com', 'business.adobe.com'], - queryIndexCardPath: '/cc-shared/assets/query-index-cards', decorateArea, stage: { marTechUrl: 'https://assets.adobedtm.com/d4d114c60e50/a0e989131fd5/launch-2c94beadc94f-development.min.js', From 59125b864bea262ec2e7561b8d6047067ef00ab1 Mon Sep 17 00:00:00 2001 From: Suhani Jain <110388864+suhjainadobe@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:46:48 +0530 Subject: [PATCH 08/10] [hue saturation] upload image icon authored is not seen visible in mobile mode (#265) [hue saturation] upload image icon authored is not seen visible in mobile mode Resolves: MWPW-146219 Test URLs: Before: https://main--cc--adobecom.hlx.live/drafts/suhjain/document7?martech=off After: https://mwpw-146219--cc--adobecom.hlx.live/drafts/suhjain/document7?martech=off --- .../slider-tray/slider-tray.css | 35 ++++++++++--------- .../slider-tray/slider-tray.js | 2 ++ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/creativecloud/features/interactive-components/slider-tray/slider-tray.css b/creativecloud/features/interactive-components/slider-tray/slider-tray.css index f4842bb6a..6c5880085 100644 --- a/creativecloud/features/interactive-components/slider-tray/slider-tray.css +++ b/creativecloud/features/interactive-components/slider-tray/slider-tray.css @@ -187,7 +187,8 @@ border: 4px solid #3892f3; } -.interactive-enabled .step-slider-tray .sliderTray .uploadButton > input { +.interactive-enabled .step-slider-tray .sliderTray .uploadButton > input, +.interactive-enabled .step-slider-tray .sliderTray .uploadButtonMobile > input { opacity: 0; position: absolute; z-index: 2; @@ -209,8 +210,8 @@ display: inline-flex; box-sizing: content-box; margin: 16px auto 0; - background: var(--prompt-btn-fill-light); - color: var(--color-black) !important; + background: var(--prompt-input-fill); + color: var(--color-white) !important; width: auto; gap: 8px; padding: 5px 12px 5px 8px; @@ -218,30 +219,32 @@ border-radius: 8px; align-items: center; max-height: 32px; - font-size: 21px; - border: 3px solid; + font-size: 18px; + border: 1px solid #fff; text-decoration: none; position: relative; } -.interactive-enabled.light .step-slider-tray .sliderTray .menu .uploadButtonMobile { - background: var(--prompt-btn-fill); - color: var(--color-white) !important; -} - -.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu .uploadButtonMobile { +.interactive-enabled.light .step-slider-tray .sliderTray .menu .uploadButtonMobile, +.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu .uploadButtonMobile { background: var(--prompt-btn-fill-light); color: var(--color-black) !important; + border: 1px solid #000; } -.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu .uploadButtonMobile { - background: var(--prompt-btn-fill); +.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu .uploadButtonMobile { + background: var(--prompt-input-fill); color: var(--color-white) !important; + border: 1px solid #fff; } .interactive-enabled .step-slider-tray .sliderTray .uploadButton.uploadButtonMobile:hover, -.interactive-enabled .step-slider-tray .sliderTray .uploadButton.uploadButtonMobile.focusUploadButton { - border: 3px solid #1273E6; +.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu .uploadButtonMobile:hover, +.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu .uploadButtonMobile:hover, +.interactive-enabled .step-slider-tray .sliderTray .uploadButton.uploadButtonMobile.focusUploadButton, +.interactive-enabled .interactive-holder.dark.step-slider-tray .sliderTray .menu .uploadButtonMobile.focusUploadButton, +.interactive-enabled .interactive-holder.light.step-slider-tray .sliderTray .menu .uploadButtonMobile.focusUploadButton { + border: 1px solid #1273E6; } .interactive-enabled .step-slider-tray .sliderTray .menu .uploadButtonMobile.hide { @@ -251,6 +254,7 @@ .interactive-enabled .step-slider-tray .sliderTray .svg-icon-container { height: 18px; width: 18px; + display: flex; } @media screen and (min-width: 600px) { @@ -295,7 +299,6 @@ border: 3px solid transparent; box-sizing: border-box; box-shadow: 0 10px 10px rgba(0, 0, 0, 0.32); - font-size: 21px; height: 64px; position: relative; } diff --git a/creativecloud/features/interactive-components/slider-tray/slider-tray.js b/creativecloud/features/interactive-components/slider-tray/slider-tray.js index cbb1e8ec1..b6f926e72 100644 --- a/creativecloud/features/interactive-components/slider-tray/slider-tray.js +++ b/creativecloud/features/interactive-components/slider-tray/slider-tray.js @@ -94,9 +94,11 @@ function createUploadButton(details, picture, sliderTray, menu) { appendSVGToButton(picture, labelBtn); const clone = labelBtn.cloneNode(true); clone.classList.add('uploadButtonMobile'); + const mobileInput = clone.querySelector('.inputFile'); menu.append(clone); sliderTray.append(labelBtn); applyAccessibility(btn, labelBtn); + applyAccessibility(mobileInput, clone); } function applyAccessibility(inputEle, target) { From 0e5242666dc0bb3db580a0059db36c3be6d2e44f Mon Sep 17 00:00:00 2001 From: Aishwarya Mathuria Date: Wed, 17 Apr 2024 10:25:38 +0530 Subject: [PATCH 09/10] MWPW-144519 | Increasing unit test coverage (#269) Increasing unit test coverage to be > 90 --- test/blocks/changeBg/changeBg.test.js | 13 -- .../interactive-marquee.test.js | 3 +- .../interactive-marquee/mocks/body.html | 173 ++++++++++++++++++ .../interactive-marquee/mocks/container.html | 26 --- test/features/changeBg/changeBg.test.js | 19 ++ .../changeBg/mocks/body.html} | 2 +- .../firefly/firefly-interactive.test.js | 36 +++- test/features/firefly/mocks/body.html | 54 +++++- .../genfill/genfill-interactive.test.js | 13 ++ test/features/genfill/mocks/genfill.html | 2 +- .../start-over/mocks/body.html | 4 +- .../start-over/start-over.test.js | 4 +- test/scripts/decorate.test.js | 31 ++++ test/scripts/mocks/body.html | 59 ++++++ test/scripts/scripts.test.js | 22 +++ 15 files changed, 411 insertions(+), 50 deletions(-) delete mode 100644 test/blocks/changeBg/changeBg.test.js create mode 100644 test/blocks/interactive-marquee/mocks/body.html delete mode 100644 test/blocks/interactive-marquee/mocks/container.html create mode 100644 test/features/changeBg/changeBg.test.js rename test/{blocks/changeBg/mocks/index.html => features/changeBg/mocks/body.html} (99%) create mode 100644 test/scripts/decorate.test.js create mode 100644 test/scripts/mocks/body.html create mode 100644 test/scripts/scripts.test.js diff --git a/test/blocks/changeBg/changeBg.test.js b/test/blocks/changeBg/changeBg.test.js deleted file mode 100644 index 054da55d5..000000000 --- a/test/blocks/changeBg/changeBg.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import { readFile } from '@web/test-runner-commands'; -import { expect } from '@esm-bundle/chai'; -import changeBg from '../../../creativecloud/features/changeBg/changeBg.js'; - -document.body.innerHTML = await readFile({ path: './mocks/index.html' }); -describe('interactive-marquee', async () => { - const marquee = document.querySelector('.changebg'); - await changeBg(marquee); - it('should exist', () => { - const changebackgroundmarquee = document.querySelector('ft-changebackgroundmarquee'); - expect(changebackgroundmarquee).to.exist; - }); -}); diff --git a/test/blocks/interactive-marquee/interactive-marquee.test.js b/test/blocks/interactive-marquee/interactive-marquee.test.js index 1ec1c6d54..dfcf7623c 100644 --- a/test/blocks/interactive-marquee/interactive-marquee.test.js +++ b/test/blocks/interactive-marquee/interactive-marquee.test.js @@ -1,7 +1,7 @@ import { readFile } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; -document.body.innerHTML = await readFile({ path: './mocks/container.html' }); +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); const { setLibs } = await import('../../../creativecloud/scripts/utils.js'); const { default: init } = await import('../../../creativecloud/blocks/interactive-marquee/interactive-marquee.js'); @@ -11,6 +11,7 @@ describe('interactive marquee', () => { setLibs('https://milo.adobe.com/libs'); await init(im); }); + it('interactive marquee light variant should exist', () => { const light = im.classList.contains('light'); expect(light).to.true; diff --git a/test/blocks/interactive-marquee/mocks/body.html b/test/blocks/interactive-marquee/mocks/body.html new file mode 100644 index 000000000..31340c11f --- /dev/null +++ b/test/blocks/interactive-marquee/mocks/body.html @@ -0,0 +1,173 @@ +
+
+
+ + + +
+
+
+
+

+ + + Photoshop +

+

Edit photos online with Adobe Photoshop

+

Add, remove, or expand content in images right in your browser with Photoshop on the web. Included in every Photoshop plan.

+

Free Trial Explore Photoshop online

+
+
+ + Jungle + +
+
+
+
+
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+
+ +
+
+ +
+ + +
+
+ + Inserting image... + + + Inserting image... + + + Inserting image... + +
+
+
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+ + Inserting image... + + + Inserting image... + + + Inserting image... + +
+
+ +
+
#31A8FF
+
#7F66E6
+
#31F7FF,#31A8FF,#31A8FF
+
+ +
+
+ + Inserting image... + + + Inserting image... + + + Inserting image... + +
+
+
+
+ + Inserting image... + +
+
+ + Inserting image... + +
+
+ + Inserting image... + + + Inserting image... + + Inserting image... + +
+
+
diff --git a/test/blocks/interactive-marquee/mocks/container.html b/test/blocks/interactive-marquee/mocks/container.html deleted file mode 100644 index 83e363e17..000000000 --- a/test/blocks/interactive-marquee/mocks/container.html +++ /dev/null @@ -1,26 +0,0 @@ -
-
-
- - - -
-
-
-
-

- - - Photoshop -

-

Edit photos online with Adobe Photoshop

-

Add, remove, or expand content in images right in your browser with Photoshop on the web. Included in every Photoshop plan.

-

Free Trial Explore Photoshop online

-
-
- - Jungle - -
-
-
diff --git a/test/features/changeBg/changeBg.test.js b/test/features/changeBg/changeBg.test.js new file mode 100644 index 000000000..9c25bb3a1 --- /dev/null +++ b/test/features/changeBg/changeBg.test.js @@ -0,0 +1,19 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; +import { setLibs } from '../../../creativecloud/scripts/utils.js'; + +setLibs('/libs'); +const { default: init } = await import('../../../creativecloud/blocks/interactive-marquee/interactive-marquee.js'); + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +describe('Change Background Marquee', async () => { + const cmq = document.querySelector('.changebg'); + before(async () => { + await init(cmq); + }); + + it('Change background should be decorated', () => { + const changebackgroundmarquee = document.querySelector('ft-changebackgroundmarquee'); + expect(changebackgroundmarquee).to.exist; + }); +}); diff --git a/test/blocks/changeBg/mocks/index.html b/test/features/changeBg/mocks/body.html similarity index 99% rename from test/blocks/changeBg/mocks/index.html rename to test/features/changeBg/mocks/body.html index 3ccc5f2a3..2208a722b 100644 --- a/test/blocks/changeBg/mocks/index.html +++ b/test/features/changeBg/mocks/body.html @@ -220,4 +220,4 @@
-
\ No newline at end of file +
diff --git a/test/features/firefly/firefly-interactive.test.js b/test/features/firefly/firefly-interactive.test.js index 2334ec68c..458eee41a 100644 --- a/test/features/firefly/firefly-interactive.test.js +++ b/test/features/firefly/firefly-interactive.test.js @@ -4,7 +4,6 @@ import { setLibs } from '../../../creativecloud/scripts/utils.js'; import waitForElement from '../../helpers/waitForElement.js'; setLibs('/libs'); - const { default: init } = await import('../../../creativecloud/blocks/interactive-marquee/interactive-marquee.js'); describe('firefly-marquee', () => { @@ -63,6 +62,21 @@ describe('firefly-marquee', () => { const texteffectPrompt = await waitForElement('.promptbar'); expect(texteffectPrompt).to.exist; }); + + it('Clicking on text to image option in interactive selector should diplay text to image detail', async () => { + const selector = await waitForElement('.selector-tray'); + const button = selector.querySelectorAll('.options')[0]; + button.click(); + const textToImg = await waitForElement('.promptbar'); + expect(textToImg).to.exist; + }); + + it('Dispatching key codes to prompt input', async () => { + const promptinput = document.querySelector('#promptinput'); + promptinput.click(); + const eventSpace = new KeyboardEvent('keydown', { key: 'Space', code: 'Space', which: 32, keyCode: 32 }); + promptinput.dispatchEvent(eventSpace); + }); }); describe('firefly-text-effect-marquee', () => { @@ -93,6 +107,7 @@ 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')); + await init(document.querySelector('.firefly.first-genfill')); }); it('Prompt should exist', async () => { @@ -112,3 +127,22 @@ describe('firefly-text-to-image-marquee', () => { expect(enticementArrow).to.exist; }); }); + +describe('firefly-genfill', () => { + before(async () => { + document.body.innerHTML = await readFile({ path: './mocks/body.html' }); + await init(document.querySelector('.firefly.first-genfill')); + }); + + 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; + }); + + it('Clicking on genfill redirects to susi', () => { + const ffmq = document.querySelector('.firefly.first-genfill'); + ffmq.querySelector('#genfill').click(); + }); +}); diff --git a/test/features/firefly/mocks/body.html b/test/features/firefly/mocks/body.html index 9865a5716..199ef35a6 100644 --- a/test/features/firefly/mocks/body.html +++ b/test/features/firefly/mocks/body.html @@ -1,7 +1,7 @@
-
+
-
linear-gradient(90deg, rgba(238,174,202,1) 0%, rgba(148,187,233,1) 100%
+
#fff
@@ -46,7 +46,7 @@

Your imagination’s new best friend.

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

Your imagination’s new best friend.

-
linear-gradient(90deg, rgba(238,174,202,1) 0%, rgba(148,187,233,1) 100% +
#fff
@@ -126,3 +126,49 @@

Your imagination’s new best friend.

+
+
+
+
#fff
+
+
+
+

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

+

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

+

https://firefly.adobe.com/upload/inpaint#_dntGenerative Fill

+

Prompt Used[Turquoise water reflecting a starry sky]|Try Generative Fill

+

+

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

+

https://firefly.adobe.com/generate/images#_dnt Text to Image

+

Dog in a sweater, primary colors, big smile|Generate

+

+ + + + + + +

+

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

+

https://firefly.adobe.com/generate/font-styles#_dnt|Text Effects

+

Tiger Fur|Generate

+

+ + + + + + +

+
+
+
+
diff --git a/test/features/genfill/genfill-interactive.test.js b/test/features/genfill/genfill-interactive.test.js index 6639ed01d..f15352fa8 100644 --- a/test/features/genfill/genfill-interactive.test.js +++ b/test/features/genfill/genfill-interactive.test.js @@ -4,31 +4,44 @@ import { expect } from '@esm-bundle/chai'; document.body.innerHTML = await readFile({ path: './mocks/genfill.html' }); const { setLibs } = await import('../../../creativecloud/scripts/utils.js'); const { default: init } = await import('../../../creativecloud/blocks/interactive-marquee/interactive-marquee.js'); +function delay(ms) { + return new Promise((res) => { setTimeout(() => { res(); }, ms); }); +} describe('genfill variant of interactive marquee', () => { const im = document.querySelector('.interactive-marquee'); + before(async () => { setLibs('https://milo.adobe.com/libs'); await init(im); }); + it('desktop media should exist', () => { const desktop = im.querySelector('.desktop-only'); expect(desktop).to.exist; }); + it('tablet media should exist', () => { const tablet = im.querySelector('.tablet-only'); expect(tablet).to.exist; }); + it('mobile media should exist', () => { const mobile = im.querySelector('.mobile-only'); expect(mobile).to.exist; }); + it('should have proper enticement', () => { const ent = im.querySelector('.enticement'); expect(ent).to.exist; expect(ent.querySelector('.enticement-text')).to.exist; expect(ent.querySelector('.enticement-arrow')).to.exist; }); + + it('should autocycle', async () => { + await delay(500); + }); + it('should implement click functionality and analytics', () => { const a = im.querySelector('.desktop-only a'); expect(a.getAttribute('daa-ll').includes('Generate Jungle')).to.true; diff --git a/test/features/genfill/mocks/genfill.html b/test/features/genfill/mocks/genfill.html index 3f57872f8..8edae4aed 100644 --- a/test/features/genfill/mocks/genfill.html +++ b/test/features/genfill/mocks/genfill.html @@ -25,7 +25,7 @@

Edit photos online with Adobe P

See it in action

-

2000|0

+

20|0

diff --git a/test/features/interactive-components/start-over/mocks/body.html b/test/features/interactive-components/start-over/mocks/body.html index 4f301305b..4024c3a95 100644 --- a/test/features/interactive-components/start-over/mocks/body.html +++ b/test/features/interactive-components/start-over/mocks/body.html @@ -23,13 +23,13 @@

Everyone can. Photoshop.

-

Beautiful full moon | Generate | Right

+

Beautiful full moon | Generate

-

Start Over

+

Start Over | 2

diff --git a/test/features/interactive-components/start-over/start-over.test.js b/test/features/interactive-components/start-over/start-over.test.js index d59f651c8..28863e00b 100644 --- a/test/features/interactive-components/start-over/start-over.test.js +++ b/test/features/interactive-components/start-over/start-over.test.js @@ -1,4 +1,4 @@ -import { readFile } from '@web/test-runner-commands'; +import { readFile, setViewport } from '@web/test-runner-commands'; import { expect } from '@esm-bundle/chai'; document.body.innerHTML = await readFile({ path: './mocks/body.html' }); @@ -13,6 +13,8 @@ describe('Start Over', () => { setLibs('https://milo.adobe.com/libs'); im = document.querySelector('.interactive-metadata'); ib = document.querySelector('.marquee'); + setViewport({ width: 1500, height: 1500 }); + window.dispatchEvent(new Event('resize')); await init(im); }); it('should render generate layer', () => { diff --git a/test/scripts/decorate.test.js b/test/scripts/decorate.test.js new file mode 100644 index 000000000..1e6491af8 --- /dev/null +++ b/test/scripts/decorate.test.js @@ -0,0 +1,31 @@ +import { readFile, setViewport } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; +import defineDeviceByScreenSize from '../../creativecloud/scripts/decorate.js'; + +function delay(ms) { + return new Promise((res) => { setTimeout(() => { res(); }, ms); }); +} + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +describe('Scripts', () => { + it('Get devide screen type - Tablet', () => { + const sw = defineDeviceByScreenSize(); + expect(sw).to.equal('TABLET'); + }); + + it('Get devide screen type - Desktop', async () => { + setViewport({ width: 1500, height: 1500 }); + window.dispatchEvent(new Event('resize')); + await delay(200); + const sw = defineDeviceByScreenSize(); + expect(sw).to.equal('DESKTOP'); + }); + + it('Get devide screen type - Mobile', async () => { + setViewport({ width: 500, height: 500 }); + window.dispatchEvent(new Event('resize')); + await delay(200); + const sw = defineDeviceByScreenSize(); + expect(sw).to.equal('MOBILE'); + }); +}); diff --git a/test/scripts/mocks/body.html b/test/scripts/mocks/body.html new file mode 100644 index 000000000..7546bc120 --- /dev/null +++ b/test/scripts/mocks/body.html @@ -0,0 +1,59 @@ +
+
+
+
+
+ + + + + + +
+
+
+
+

+ + + + + + Photoshop +

+

Edit photos online with Adobe Photoshop

+

Add, remove, or expand content in images right in your browser with Photoshop on the web. Included in every Photoshop plan.

+

Free Trial Explore Photoshop online

+
+
+

See it in action

+

20|0

+

+ + + + + Generate Jungle + +

+

+ + + + + Generate Pond + +

+

+ + + + + Generate Jaguar + +

+
+
+
+
+
diff --git a/test/scripts/scripts.test.js b/test/scripts/scripts.test.js new file mode 100644 index 000000000..e8f6dad02 --- /dev/null +++ b/test/scripts/scripts.test.js @@ -0,0 +1,22 @@ +import { readFile } from '@web/test-runner-commands'; +import { expect } from '@esm-bundle/chai'; + +function delay(ms) { + return new Promise((res) => { setTimeout(() => { res(); }, ms); }); +} + +document.body.innerHTML = await readFile({ path: './mocks/body.html' }); +describe('Scripts', () => { + before(async () => { + await import('../../creativecloud/scripts/scripts.js'); + delay(200); + }); + + it('First image should eager load', () => { + expect(document.querySelector('.interactive-marquee img').loading).to.equal('eager'); + }); + + it('Replaced dot media', () => { + expect(document.querySelector('.interactive-marquee img[src*="./media_"]')).to.be.null; + }); +}); From 0eaa0c662346101ab2274e836c7860ebda386a0f Mon Sep 17 00:00:00 2001 From: Aishwarya Mathuria Date: Wed, 17 Apr 2024 10:26:29 +0530 Subject: [PATCH 10/10] MWPW-142972 | Integrating review comments (#267) Integrating review comments for delay authoring of Start over button. 0-99 will be considered as sec and 99+ will be ms --- .../blocks/interactive-metadata/interactive-metadata.js | 2 +- .../interactive-components/start-over/start-over.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/creativecloud/blocks/interactive-metadata/interactive-metadata.js b/creativecloud/blocks/interactive-metadata/interactive-metadata.js index 136097446..0df5d72cc 100644 --- a/creativecloud/blocks/interactive-metadata/interactive-metadata.js +++ b/creativecloud/blocks/interactive-metadata/interactive-metadata.js @@ -149,7 +149,7 @@ async function handleLayerDisplay(stepInfo) { await handleImageTransition(stepInfo); await loadAllImgs(currLayer.querySelectorAll('img[src*="media_"]')); await decorateDefaultLinkAnalytics(currLayer); - if (prevStepIndex) stepInfo.target.classList.remove(`step-${stepInfo.stepList[prevStepIndex]}`); + if (prevStepIndex !== stepInfo.stepIndex) stepInfo.target.classList.remove(`step-${stepInfo.stepList[prevStepIndex]}`); if (clsLayer) clsLayer.remove(); stepInfo.target.classList.add(`step-${stepInfo.stepName}`); currLayer.classList.add('show-layer'); diff --git a/creativecloud/features/interactive-components/start-over/start-over.js b/creativecloud/features/interactive-components/start-over/start-over.js index 25e5aec3c..375160fe0 100644 --- a/creativecloud/features/interactive-components/start-over/start-over.js +++ b/creativecloud/features/interactive-components/start-over/start-over.js @@ -5,7 +5,7 @@ function btnLoadDelay(layer, button, delay, once = true) { entries.forEach(async (entry) => { if (entry.isIntersecting) { if (once) observer.unobserve(entry.target); - setTimeout(() => { button.style.display = 'flex'; }, parseInt(delay, 10)); + setTimeout(() => { button.style.display = 'flex'; }, delay); } }); }); @@ -37,8 +37,11 @@ export default async function stepInit(data) { data.el.dispatchEvent(new CustomEvent(data.nextStepEvent)); }); } - if (delay) btnLoadDelay(layer, startOverCTA, delay); - else startOverCTA.style.display = 'flex'; + if (delay) { + const dInt = parseInt(delay, 10); + const normDelay = dInt > 99 ? dInt : (dInt * 1000); + btnLoadDelay(layer, startOverCTA, normDelay); + } else startOverCTA.style.display = 'flex'; layer.append(startOverCTA); return layer; }