From 087bc5a889f85c7a536241fde6931dc07506cd9c Mon Sep 17 00:00:00 2001 From: Emmanuel Pelletier Date: Tue, 7 May 2024 20:40:32 +0200 Subject: [PATCH] api/gaufre: stop using an iframe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iframe was great because we controlled our page context to style things easily, handle assets easily. But since it's not on the same domain as the services consuming it, it implied configuration here and there. Also some behaviors were annoying to implement (for example, keyboard navigation). I'm sure everything we do is possible via iframe but I feel like I'll go from barrier to barrier at every new thing we want to do… I feel like, at the cost of handling style-conflicts, just rendering everything in the real page context is more future-proof. --- website/public/api/v1/gaufre.js | 92 ++++--- website/src/components/GaufrePage.astro | 349 ++++++++++++------------ 2 files changed, 216 insertions(+), 225 deletions(-) diff --git a/website/public/api/v1/gaufre.js b/website/public/api/v1/gaufre.js index 162e225..8601ca1 100644 --- a/website/public/api/v1/gaufre.js +++ b/website/public/api/v1/gaufre.js @@ -4,76 +4,78 @@ if ("requestIdleCallback" in window) { requestIdleCallback(() => { - appendIframe() + appendPopup() }) } document.body.addEventListener("click", (event) => { if (!event.target.classList || !event.target.classList.contains(BUTTON_CLASS)) { const buttons = document.querySelectorAll(`.${BUTTON_CLASS}`) - buttons.forEach((b) => b.classList.remove("lasuite--opened")) - hideIframe() + buttons.forEach((b) => b.classList.remove("lasuite--gaufre-opened")) + hidePopup() return } const button = event.target - button.classList.toggle("lasuite--opened") - if (button.classList.contains("lasuite--opened")) { - showIframe(button) + button.classList.toggle("lasuite--gaufre-opened") + if (button.classList.contains("lasuite--gaufre-opened")) { + showPopup(button) } else { - hideIframe() + hidePopup() } }) document.addEventListener("keyup", (event) => { - if (event.key === "Escape") { - hideIframe() - } - }) - - window.addEventListener("message", (event) => { - if (event.data === "lasuite-close-services-iframe") { - hideIframe() + if (event.key === "Escape" && document.activeElement.closest(".lagaufre")) { + hidePopup() } }) window.addEventListener("resize", () => { - const iframe = document.querySelector(`#lasuite-gaufre-iframe.lasuite--opened`) - if (!iframe) { + const popup = document.querySelector(`#lasuite-gaufre-popup.lasuite--gaufre-opened`) + if (!popup) { return } - const button = document.querySelector(`.${BUTTON_CLASS}.lasuite--opened`) + const button = document.querySelector(`.${BUTTON_CLASS}.lasuite--gaufre-opened`) if (!button) { return } - iframe.style.cssText = getIframePositionStyle(button) + popup.style.cssText = getPopupPositionStyle(button) }) - const appendIframe = () => { - if (document.querySelector(`#lasuite-gaufre-iframe`)) { + const appendPopup = () => { + if (document.querySelector(`#lasuite-gaufre-popup`)) { return } const scriptTag = document.querySelector(`#lasuite-gaufre-script`) if (!scriptTag) { console.log( - "La Suite numérique: Gaufre script tag not found, please check out the documentation", + "La Suite numérique: Gaufre script tag not found, make sure the script has id 'lasuite-gaufre-script'.", ) return } - const iframe = document.createElement("iframe") - iframe.title = "Services de La Suite numérique" - iframe.id = "lasuite-gaufre-iframe" - iframe.width = "304" - iframe.height = "360" - iframe.style.cssText = "display: none !important" - const { host, protocol, searchParams } = new URL(scriptTag.src) + const popup = document.createElement("div") + popup.id = "lasuite-gaufre-popup" + popup.width = "304" + popup.height = "360" + popup.style.cssText = "display: none !important" + const { host, protocol, searchParams, origin } = new URL(scriptTag.src) const local = searchParams.get("type") === "local" const lang = ["en"].includes(searchParams.get("lang")) ? searchParams.get("lang") : null - iframe.src = `${protocol}//${host}/api/v1/${(!!lang && `${lang}/`) || ""}gaufre${(!!local && "/local") || ""}` - document.body.appendChild(iframe) + fetch( + `${protocol}//${host}/api/v1/${(!!lang && `${lang}/`) || ""}gaufre${(!!local && "/local") || ""}`, + ) + .then((res) => res.text()) + .then((html) => { + html = html.replace(/(src=|href=|url\()"\//g, `$1"${origin}/`) + const parser = new DOMParser() + const popupDocument = parser.parseFromString(html, "text/html") + popup.innerHTML = popupDocument.body.innerHTML + document.body.appendChild(popup) + }) } - const getIframePositionStyle = (button) => { + const getPopupPositionStyle = (button) => { const buttonCoords = button.getBoundingClientRect() const isSmallScreen = window.innerWidth <= 400 return ` @@ -95,27 +97,27 @@ ` } - const showIframe = (button) => { - const iframe = document.querySelector(`#lasuite-gaufre-iframe`) - if (!iframe) { - appendIframe() + const showPopup = (button) => { + const popup = document.querySelector(`#lasuite-gaufre-popup`) + if (!popup) { + appendPopup() } - iframe.style.cssText = getIframePositionStyle(button) - iframe.classList.add("lasuite--opened") + popup.style.cssText = getPopupPositionStyle(button) + popup.classList.add("lasuite--gaufre-opened") lastFocusedButton = button setTimeout(() => { - iframe.focus() + popup.querySelector(".js-lagaufre-keyboard-anchor").focus() }, 0) } - const hideIframe = () => { - const iframe = document.querySelector(`#lasuite-gaufre-iframe`) - if (iframe) { - iframe.style.cssText = "display: none !important" - iframe.classList.remove("lasuite--opened") + const hidePopup = () => { + const popup = document.querySelector(`#lasuite-gaufre-popup`) + if (popup) { + popup.style.cssText = "display: none !important" + popup.classList.remove("lasuite--gaufre-opened") } if (lastFocusedButton) { - lastFocusedButton.classList.remove("lasuite--opened") + lastFocusedButton.classList.remove("lasuite--gaufre-opened") lastFocusedButton.focus() lastFocusedButton = null } diff --git a/website/src/components/GaufrePage.astro b/website/src/components/GaufrePage.astro index 67be06c..7a4ca34 100644 --- a/website/src/components/GaufrePage.astro +++ b/website/src/components/GaufrePage.astro @@ -1,4 +1,10 @@ --- +/** + * this page is meant to be directly included on a service page via the gaufre script tag + * + * This is not rendered in an iframe! it's included directly in the host page with a fetch request. + * Every css rule is scoped and marked as important to avoid style conflicts with the host page. + */ import { Image } from "astro:assets" const logos = import.meta.glob<{ default: ImageMetadata }>("/src/assets/logos/*.{svg,png,jpg}") const { services } = Astro.props @@ -9,177 +15,167 @@ const { services } = Astro.props Services de La Suite numérique - -
-
-

Liste des services de La Suite numérique

-
    +
    + +
    +

    + Liste des services de La Suite numérique +

    +
    -