diff --git a/src/components/DecisioningEngine/consequenceAdapters/inAppMessageConsequenceAdapter.js b/src/components/DecisioningEngine/consequenceAdapters/inAppMessageConsequenceAdapter.js index 1249d8bc8..0bf3090b3 100644 --- a/src/components/DecisioningEngine/consequenceAdapters/inAppMessageConsequenceAdapter.js +++ b/src/components/DecisioningEngine/consequenceAdapters/inAppMessageConsequenceAdapter.js @@ -13,8 +13,10 @@ import { MESSAGE_IN_APP } from "../../Personalization/constants/schema"; import { TEXT_HTML } from "../../Personalization/constants/contentType"; export default (id, type, detail) => { + // TODO: add webParameters when available from the authoring UI in detail const { html, mobileParameters } = detail; + // TODO: Remove it once we have webParameters const webParameters = { info: "this is a placeholder" }; return { diff --git a/src/components/Personalization/in-app-message-actions/actions/displayIframeContent.js b/src/components/Personalization/in-app-message-actions/actions/displayIframeContent.js index 39fcacf9b..4582c1df6 100644 --- a/src/components/Personalization/in-app-message-actions/actions/displayIframeContent.js +++ b/src/components/Personalization/in-app-message-actions/actions/displayIframeContent.js @@ -11,73 +11,19 @@ governing permissions and limitations under the License. */ import { getNonce } from "../../dom-actions/dom"; -import { parseAnchor, removeElementById } from "../utils"; +import { createElement, parseAnchor, removeElementById } from "../utils"; import { TEXT_HTML } from "../../constants/contentType"; +import { assign } from "../../../../utils"; import { getEventType } from "../../constants/propositionEventType"; -const ELEMENT_TAG_CLASSNAME = "alloy-messaging-container"; -const ELEMENT_TAG_ID = "alloy-messaging-container"; - -const OVERLAY_TAG_CLASSNAME = "alloy-overlay-container"; -const OVERLAY_TAG_ID = "alloy-overlay-container"; +const ALLOY_MESSAGING_CONTAINER_ID = "alloy-messaging-container"; +const ALLOY_OVERLAY_CONTAINER_ID = "alloy-overlay-container"; const ALLOY_IFRAME_ID = "alloy-content-iframe"; const dismissMessage = () => - [ELEMENT_TAG_ID, OVERLAY_TAG_ID].forEach(removeElementById); - -// eslint-disable-next-line no-unused-vars -export const buildStyleFromParameters = (mobileParameters, webParameters) => { - const { - verticalAlign, - width, - horizontalAlign, - backdropColor, - height, - cornerRadius, - horizontalInset, - verticalInset, - uiTakeover = false - } = mobileParameters; - - const style = { - width: width ? `${width}%` : "100%", - backgroundColor: backdropColor || "rgba(0, 0, 0, 0.5)", - borderRadius: cornerRadius ? `${cornerRadius}px` : "0px", - border: "none", - position: uiTakeover ? "fixed" : "relative", - overflow: "hidden" - }; - if (horizontalAlign === "left") { - style.left = horizontalInset ? `${horizontalInset}%` : "0"; - } else if (horizontalAlign === "right") { - style.right = horizontalInset ? `${horizontalInset}%` : "0"; - } else if (horizontalAlign === "center") { - style.left = "50%"; - style.transform = "translateX(-50%)"; - } - - if (verticalAlign === "top") { - style.top = verticalInset ? `${verticalInset}%` : "0"; - } else if (verticalAlign === "bottom") { - style.position = "fixed"; - style.bottom = verticalInset ? `${verticalInset}%` : "0"; - } else if (verticalAlign === "center") { - style.top = "50%"; - style.transform = `${ - horizontalAlign === "center" ? `${style.transform} ` : "" - }translateY(-50%)`; - style.display = "flex"; - style.alignItems = "center"; - style.justifyContent = "center"; - } - - if (height) { - style.height = `${height}vh`; - } else { - style.height = "100%"; - } - return style; -}; + [ALLOY_MESSAGING_CONTAINER_ID, ALLOY_OVERLAY_CONTAINER_ID].forEach( + removeElementById + ); const setWindowLocationHref = link => { window.location.assign(link); @@ -132,13 +78,6 @@ export const createIframe = (htmlContent, clickHandler) => { new Blob([htmlDocument.documentElement.outerHTML], { type: TEXT_HTML }) ); element.id = ALLOY_IFRAME_ID; - - Object.assign(element.style, { - border: "none", - width: "100%", - height: "100%" - }); - element.addEventListener("load", () => { const { addEventListener } = element.contentDocument || element.contentWindow.document; @@ -148,60 +87,203 @@ export const createIframe = (htmlContent, clickHandler) => { return element; }; -export const createContainerElement = settings => { - const { mobileParameters = {}, webParameters = {} } = settings; - const element = document.createElement("div"); - element.id = ELEMENT_TAG_ID; - element.className = `${ELEMENT_TAG_CLASSNAME}`; - Object.assign( - element.style, - buildStyleFromParameters(mobileParameters, webParameters) - ); - - return element; +const renderMessage = (iframe, webParameters, container, overlay) => { + [ + { id: ALLOY_OVERLAY_CONTAINER_ID, element: overlay }, + { id: ALLOY_MESSAGING_CONTAINER_ID, element: container }, + { id: ALLOY_IFRAME_ID, element: iframe } + ].forEach(({ id, element }) => { + const { style = {}, params = {} } = webParameters[id]; + + assign(element.style, style); + + const { + parentElement = "body", + insertionMethod = "appendChild", + enabled = true + } = params; + + const parent = document.querySelector(parentElement); + if (enabled && parent && typeof parent[insertionMethod] === "function") { + parent[insertionMethod](element); + } + }); }; -export const createOverlayElement = parameter => { - const element = document.createElement("div"); - const backdropOpacity = parameter.backdropOpacity || 0.5; - const backdropColor = parameter.backdropColor || "#FFFFFF"; - element.id = OVERLAY_TAG_ID; - element.className = `${OVERLAY_TAG_CLASSNAME}`; +export const buildStyleFromMobileParameters = mobileParameters => { + const { + verticalAlign, + width, + horizontalAlign, + backdropColor, + height, + cornerRadius, + horizontalInset, + verticalInset, + uiTakeover = false + } = mobileParameters; - Object.assign(element.style, { + const style = { + width: width ? `${width}%` : "100%", + backgroundColor: backdropColor || "rgba(0, 0, 0, 0.5)", + borderRadius: cornerRadius ? `${cornerRadius}px` : "0px", + border: "none", + position: uiTakeover ? "fixed" : "relative", + overflow: "hidden" + }; + if (horizontalAlign === "left") { + style.left = horizontalInset ? `${horizontalInset}%` : "0"; + } else if (horizontalAlign === "right") { + style.right = horizontalInset ? `${horizontalInset}%` : "0"; + } else if (horizontalAlign === "center") { + style.left = "50%"; + style.transform = "translateX(-50%)"; + } + + if (verticalAlign === "top") { + style.top = verticalInset ? `${verticalInset}%` : "0"; + } else if (verticalAlign === "bottom") { + style.position = "fixed"; + style.bottom = verticalInset ? `${verticalInset}%` : "0"; + } else if (verticalAlign === "center") { + style.top = "50%"; + style.transform = `${ + horizontalAlign === "center" ? `${style.transform} ` : "" + }translateY(-50%)`; + style.display = "flex"; + style.alignItems = "center"; + style.justifyContent = "center"; + } + + if (height) { + style.height = `${height}vh`; + } else { + style.height = "100%"; + } + return style; +}; + +export const mobileOverlay = mobileParameters => { + const { backdropOpacity, backdropColor } = mobileParameters; + const opacity = backdropOpacity || 0.5; + const color = backdropColor || "#FFFFFF"; + const style = { position: "fixed", top: "0", left: "0", width: "100%", height: "100%", background: "transparent", - opacity: backdropOpacity, - backgroundColor: backdropColor - }); + opacity, + backgroundColor: color + }; + return style; +}; - return element; +const REQUIRED_PARAMS = ["enabled", "parentElement", "insertionMethod"]; + +const isValidWebParameters = webParameters => { + if (!webParameters) { + return false; + } + + const ids = Object.keys(webParameters); + + if (!ids.includes(ALLOY_MESSAGING_CONTAINER_ID)) { + return false; + } + + if (!ids.includes(ALLOY_OVERLAY_CONTAINER_ID)) { + return false; + } + + const values = Object.values(webParameters); + + for (let i = 0; i < values.length; i += 1) { + if (!Object.prototype.hasOwnProperty.call(values[i], "style")) { + return false; + } + + if (!Object.prototype.hasOwnProperty.call(values[i], "params")) { + return false; + } + + for (let j = 0; j < REQUIRED_PARAMS.length; j += 1) { + if ( + !Object.prototype.hasOwnProperty.call( + values[i].params, + REQUIRED_PARAMS[j] + ) + ) { + return false; + } + } + } + + return true; +}; + +const generateWebParameters = mobileParameters => { + if (!mobileParameters) { + return undefined; + } + + const { uiTakeover = false } = mobileParameters; + + return { + [ALLOY_IFRAME_ID]: { + style: { + border: "none", + width: "100%", + height: "100%" + }, + params: { + enabled: true, + parentElement: "#alloy-messaging-container", + insertionMethod: "appendChild" + } + }, + [ALLOY_MESSAGING_CONTAINER_ID]: { + style: buildStyleFromMobileParameters(mobileParameters), + params: { + enabled: true, + parentElement: "body", + insertionMethod: "appendChild" + } + }, + [ALLOY_OVERLAY_CONTAINER_ID]: { + style: mobileOverlay(mobileParameters), + params: { + enabled: uiTakeover === true, + parentElement: "body", + insertionMethod: "appendChild" + } + } + }; }; export const displayHTMLContentInIframe = (settings, interact) => { dismissMessage(); const { content, contentType, mobileParameters } = settings; + let { webParameters } = settings; if (contentType !== TEXT_HTML) { return; } - const container = createContainerElement(settings); - + const container = createElement(ALLOY_MESSAGING_CONTAINER_ID); const iframe = createIframe(content, createIframeClickHandler(interact)); + const overlay = createElement(ALLOY_OVERLAY_CONTAINER_ID); - container.appendChild(iframe); + if (!isValidWebParameters(webParameters)) { + webParameters = generateWebParameters(mobileParameters); + } - if (mobileParameters.uiTakeover) { - const overlay = createOverlayElement(mobileParameters); - document.body.appendChild(overlay); - document.body.style.overflow = "hidden"; + if (!webParameters) { + return; } - document.body.appendChild(container); + + renderMessage(iframe, webParameters, container, overlay); }; export default (settings, collect) => { diff --git a/src/components/Personalization/in-app-message-actions/utils.js b/src/components/Personalization/in-app-message-actions/utils.js index b8f82bc49..aa4f42567 100644 --- a/src/components/Personalization/in-app-message-actions/utils.js +++ b/src/components/Personalization/in-app-message-actions/utils.js @@ -74,3 +74,8 @@ export const parseAnchor = anchor => { uuid }; }; +export const createElement = elementTagId => { + const element = document.createElement("div"); + element.id = elementTagId; + return element; +}; diff --git a/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayBanner.spec.js b/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayBanner.spec.js deleted file mode 100644 index 9b85d9f29..000000000 --- a/test/unit/specs/components/Personalization/in-app-message-actions/actions/displayBanner.spec.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2023 Adobe. All rights reserved. -This file is licensed to you under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. You may obtain a copy -of the License at http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under -the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS -OF ANY KIND, either express or implied. See the License for the specific language -governing permissions and limitations under the License. -*/ -import { createNode } from "../../../../../../../src/utils/dom"; -import { DIV } from "../../../../../../../src/constants/tagName"; -import displayIframeContent from "../../../../../../../src/components/Personalization/in-app-message-actions/actions/displayIframeContent"; -import { TEXT_HTML } from "../../../../../../../src/components/Personalization/constants/contentType"; - -describe("Personalization::IAM:banner", () => { - it("inserts banner into dom", async () => { - const something = createNode( - DIV, - { className: "something" }, - { - innerHTML: - "
Amet cillum consectetur elit cupidatat voluptate nisi duis et occaecat enim pariatur.
" - } - ); - - document.body.append(something); - - await displayIframeContent({ - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: false, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60 - }, - content: `Don\'t miss out on our incredible discounts and deals at our gadgets!
\n \nDon\'t miss out on our incredible discounts and deals at our gadgets!
\n \nDon\'t miss out on our incredible discounts and deals at our gadgets!
\n \nAmet cillum consectetur elit cupidatat voluptate nisi duis et occaecat enim pariatur.
" - } - ); - - document.body.append(something); - - await displayIframeContent({ - mobileParameters: { - verticalAlign: "center", - dismissAnimation: "top", - verticalInset: 0, - backdropOpacity: 0.2, - cornerRadius: 15, - horizontalInset: 0, - uiTakeover: true, - horizontalAlign: "center", - width: 80, - displayAnimation: "top", - backdropColor: "#000000", - height: 60 - }, - content: `