fix: embed non-transparent dark background on success page (#9085)

* Fix colorscheme dark on success page due to embed not detected properly there

* Use calNamespace in getEmbediframe

* Fix missing variable

* Update basic.e2e.ts

---------

Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
Hariom Balhara
2023-06-06 23:52:13 +00:00
committed by GitHub
co-authored by Omar López
parent b0c5b629d7
commit 6308955e89
10 changed files with 58 additions and 42 deletions
+1 -6
View File
@@ -1133,12 +1133,7 @@ export const EmbedDialog = () => {
const router = useRouter();
const embedUrl: string = router.query.embedUrl as string;
return (
<Dialog
name="embed"
clearQueryParamsOnClose={queryParamsForDialog}
onOpenChange={(open) => {
if (!open) window.resetEmbedStatus();
}}>
<Dialog name="embed" clearQueryParamsOnClose={queryParamsForDialog}>
{!router.query.embedType ? (
<ChooseEmbedTypesDialogContent />
) : (
+20 -16
View File
@@ -1,34 +1,38 @@
import type { EmbedThemeConfig } from "./embed-iframe";
export default function EmbedInitIframe() {
if (typeof window === "undefined" || window.isEmbed) {
return;
}
const calEmbedMode = typeof new URL(document.URL).searchParams.get("embed") === "string";
const embedNameSpaceFromQueryParam = new URL(document.URL).searchParams.get("embed");
/* Iframe Name */
window.name.includes("cal-embed");
// Namespace is initially set in query param `embed` but the query param might get lost during soft navigation
// So, we also check for the namespace in `window.name` which is set when iframe is created by embed.ts and persists for the duration of iframe's life
// Note that, window.name isn't lost during hard navigation as well. Though, hard navigation isn't something that would happen in the app, but it's critical to be able to detect embed mode even after that(just in case)
// We might just use window.name but if just in case something resets the `window.name`, we will still have the namespace in query param
const embedNamespace =
typeof embedNameSpaceFromQueryParam === "string"
? embedNameSpaceFromQueryParam
: window.name.replace(/cal-embed=(.*)/, "$1").trim();
window.isEmbed = () => {
// Once an embed mode always an embed mode
return calEmbedMode;
};
window.resetEmbedStatus = () => {
try {
// eslint-disable-next-line @calcom/eslint/avoid-web-storage
window.sessionStorage.removeItem("calEmbedMode");
} catch (e) {}
// By default namespace is "". That would also work if we just check the type of variable
return typeof embedNamespace == "string";
};
window.getEmbedTheme = () => {
// Note that embedStore.theme is lost if hard navigation occurs.(Though, it isn't something that we expect to happen normally)
if (window.CalEmbed.embedStore.theme) {
// It is important to ensure that the theme is consistent during browsing so that ThemeProvider doesn't get different themes to show and it avoids theme switching.
return window.CalEmbed.embedStore.theme;
}
const url = new URL(document.URL);
return url.searchParams.get("theme") as "light" | "dark" | null;
return url.searchParams.get("theme") as EmbedThemeConfig | null;
};
window.getEmbedNamespace = () => {
const url = new URL(document.URL);
const namespace = url.searchParams.get("embed");
return namespace;
return embedNamespace;
};
window.CalEmbed = window.CalEmbed || {};
+1 -1
View File
@@ -159,7 +159,7 @@
<div id="namespaces-test">
<div class="inline-embed-container" id="cal-booking-place-default">
<h3>
<a href="?only=ns:default">[Dark Theme][Guests(janedoe@example.com and test@example.com)]</a>
<a href="?only=ns:default">[Dark Theme][Guests(janedoe@example.com and test@example.com)](Default Namespace)</a>
</h3>
<button onclick="Cal('ui',{theme:'light'})">Toggle to Light</button>
+2 -3
View File
@@ -1,6 +1,5 @@
import type { GlobalCal, GlobalCalWithoutNs } from "./src/embed";
import type { GlobalCal } from "./src/embed";
type A = GlobalCalWithoutNs;
const Cal = window.Cal as GlobalCal;
const callback = function (e) {
const detail = e.detail;
@@ -8,7 +7,7 @@ const callback = function (e) {
};
const searchParams = new URL(document.URL).searchParams;
const only = window.only;
const only = searchParams.get("only");
if (only === "all" || only === "ns:default") {
Cal("init", {
@@ -34,7 +34,15 @@ export const getBooking = async (bookingId: string) => {
return booking;
};
export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname: string }) => {
export const getEmbedIframe = async ({
calNamespace,
page,
pathname,
}: {
calNamespace: string;
page: Page;
pathname: string;
}) => {
// We can't seem to access page.frame till contentWindow is available. So wait for that.
const iframeReady = await page.evaluate(() => {
return new Promise((resolve) => {
@@ -66,7 +74,7 @@ export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname:
// We just verified that iframeReady is true here, so obviously embedIframe is not null
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const embedIframe = page.frame("cal-embed")!;
const embedIframe = page.frame(`cal-embed=${calNamespace}`)!;
const u = new URL(embedIframe.url());
if (u.pathname === pathname + "/embed") {
return embedIframe;
@@ -32,7 +32,7 @@ async function bookFirstFreeUserEventThroughEmbed({
await embedButtonLocator.click();
const embedIframe = await getEmbedIframe({ page, pathname: "/free" });
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free" });
await expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
pathname: "/free",
@@ -58,12 +58,12 @@ test.describe("Popup Tests", () => {
const calNamespace = "prerendertestLightTheme";
await addEmbedListeners(calNamespace);
await page.goto("/?only=prerender-test");
let embedIframe = await getEmbedIframe({ page, pathname: "/free" });
let embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free" });
expect(embedIframe).toBeFalsy();
await page.click('[data-cal-link="free?light&popup"]');
embedIframe = await getEmbedIframe({ page, pathname: "/free" });
embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free" });
await expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
pathname: "/free",
@@ -93,7 +93,7 @@ test.describe("Popup Tests", () => {
await page.goto(`/?popupRescheduleId=${booking.uid}`);
await page.click('[data-cal-namespace="popupReschedule"]');
const embedIframe = await getEmbedIframe({ page, pathname: booking.eventSlug });
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: booking.eventSlug });
if (!embedIframe) {
throw new Error("Embed iframe not found");
}
@@ -117,12 +117,20 @@ test.describe("Popup Tests", () => {
const calNamespace = "routingFormAuto";
await addEmbedListeners(calNamespace);
await page.goto("/?only=prerender-test");
let embedIframe = await getEmbedIframe({ page, pathname: "/forms/948ae412-d995-4865-875a-48302588de03" });
let embedIframe = await getEmbedIframe({
calNamespace,
page,
pathname: "/forms/948ae412-d995-4865-875a-48302588de03",
});
expect(embedIframe).toBeFalsy();
await page.click(
`[data-cal-namespace=${calNamespace}][data-cal-link="forms/948ae412-d995-4865-875a-48302588de03"]`
);
embedIframe = await getEmbedIframe({ page, pathname: "/forms/948ae412-d995-4865-875a-48302588de03" });
embedIframe = await getEmbedIframe({
calNamespace,
page,
pathname: "/forms/948ae412-d995-4865-875a-48302588de03",
});
if (!embedIframe) {
throw new Error("Routing Form embed iframe not found");
}
@@ -12,8 +12,9 @@ test("Inline Iframe - Configured with Dark Theme", async ({
await deleteAllBookingsByEmail("embed-user@example.com");
await addEmbedListeners("");
await page.goto("/?only=ns:default");
const embedIframe = await getEmbedIframe({ page, pathname: "/pro" });
expect(embedIframe).toBeEmbedCalLink("", getActionFiredDetails, {
const calNamespace = "";
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/pro" });
expect(embedIframe).toBeEmbedCalLink(calNamespace, getActionFiredDetails, {
pathname: "/pro",
searchParams: {
theme: "dark",
@@ -11,7 +11,8 @@ type Theme = "dark" | "light";
export type EmbedThemeConfig = Theme | "auto";
export type UiConfig = {
hideEventTypeDetails?: boolean;
theme?: EmbedThemeConfig;
// If theme not provided we would get null
theme?: EmbedThemeConfig | null;
styles?: EmbedStyles & EmbedNonStylesConfig;
//TODO: Extract from tailwind the list of all custom variables and support them in auto-completion as well as runtime validation. Followup with listing all variables in Embed Snippet Generator UI.
cssVarsPerTheme?: Record<Theme, Record<string, string>>;
@@ -46,9 +47,8 @@ declare global {
};
CalComPageStatus: string;
isEmbed?: () => boolean;
resetEmbedStatus: () => void;
getEmbedNamespace: () => string | null;
getEmbedTheme: () => "dark" | "light" | null;
getEmbedTheme: () => EmbedThemeConfig | null;
}
}
@@ -434,7 +434,7 @@ if (isBrowser) {
// Exposes certain global variables/fns that are used by the app to get interface with the embed.
embedInit();
const url = new URL(document.URL);
embedStore.theme = window?.getEmbedTheme?.() as UiConfig["theme"];
embedStore.theme = window?.getEmbedTheme?.();
if (url.searchParams.get("prerender") !== "true" && window?.isEmbed?.()) {
log("Initializing embed-iframe");
// HACK
+1 -1
View File
@@ -215,7 +215,7 @@ export class Cal {
}) {
const iframe = (this.iframe = document.createElement("iframe"));
iframe.className = "cal-embed";
iframe.name = "cal-embed";
iframe.name = `cal-embed=${this.namespace}`;
const config = this.getConfig();
const { iframeAttrs, ...restQueryObject } = queryObject;
@@ -12,7 +12,8 @@ test.describe("Inline Embed", () => {
//TODO: Do it with page.goto automatically
await addEmbedListeners("");
await page.goto("/");
const embedIframe = await getEmbedIframe({ page, pathname: "/pro" });
const calNamespace = "";
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/pro" });
expect(embedIframe).toBeEmbedCalLink("", getActionFiredDetails, {
pathname: "/pro",
searchParams: {