diff --git a/apps/web/playwright/embed-code-generator.e2e.ts b/apps/web/playwright/embed-code-generator.e2e.ts index 28a0b4b7c2..f7ced620e0 100644 --- a/apps/web/playwright/embed-code-generator.e2e.ts +++ b/apps/web/playwright/embed-code-generator.e2e.ts @@ -424,6 +424,10 @@ async function expectValidHtmlEmbedSnippet( expect(embedCode).toContain(orgSlug); } + // Html/VanillaJS embed needs namespace to call an instruction + // Verify Cal.ns.abc("ui") or Cal.ns["abc"]("ui") + expect(embedCode).toMatch(/.*Cal\.ns[^(]+\("ui/); + const dom = parse(embedCode); const scripts = dom.getElementsByTagName("script"); assertThatCodeIsValidVanillaJsCode(scripts[0].innerText); @@ -492,6 +496,8 @@ async function expectValidReactEmbedSnippet( expect(embedCode).toContain( embedType === "floating-popup" ? "floatingButton" : embedType === "inline" ? ` { @@ -67,7 +67,7 @@ export const Codes = { useEffect(()=>{ (async function () { const cal = await getCalApi(${argumentForGetCalApi ? JSON.stringify(argumentForGetCalApi) : ""}); - ${getApiName({ namespace, mainApiName: "cal" })}("floatingButton", ${floatingButtonArg}); + ${getApiNameForReactSnippet({ mainApiName: "cal" })}("floatingButton", ${floatingButtonArg}); ${uiInstructionCode} })(); }, []) @@ -119,7 +119,7 @@ export const Codes = { previewState: PreviewState; namespace: string; }) => { - return code`${getApiName({ namespace })}("inline", { + return code`${getApiNameForVanillaJsSnippet({ namespace, mainApiName: "Cal" })}("inline", { elementOrSelector:"#my-cal-inline", calLink: "${calLink}", layout: "${previewState.layout}" @@ -137,7 +137,10 @@ export const Codes = { uiInstructionCode: string; namespace: string; }) => { - return code`${getApiName({ namespace, mainApiName: "Cal" })}("floatingButton", ${floatingButtonArg}); + return code`${getApiNameForVanillaJsSnippet({ + namespace, + mainApiName: "Cal", + })}("floatingButton", ${floatingButtonArg}); ${uiInstructionCode}`; }, "element-click": ({ diff --git a/packages/features/embed/lib/EmbedTabs.tsx b/packages/features/embed/lib/EmbedTabs.tsx index 913b5277f6..1f131ba363 100644 --- a/packages/features/embed/lib/EmbedTabs.tsx +++ b/packages/features/embed/lib/EmbedTabs.tsx @@ -10,7 +10,7 @@ import { TextArea } from "@calcom/ui"; import type { EmbedFramework, EmbedType, PreviewState } from "../types"; import { Codes, doWeNeedCalOriginProp } from "./EmbedCodes"; import { embedLibUrl, EMBED_PREVIEW_HTML_URL } from "./constants"; -import { getApiName } from "./getApiName"; +import { getApiNameForReactSnippet, getApiNameForVanillaJsSnippet } from "./getApiName"; import { getDimension } from "./getDimension"; import { useEmbedCalOrigin } from "./hooks"; @@ -181,7 +181,7 @@ const getEmbedTypeSpecificString = ({ }; if (embedFramework === "react") { uiInstructionStringArg = { - apiName: getApiName({ namespace, mainApiName: "cal" }), + apiName: getApiNameForReactSnippet({ mainApiName: "cal" }), theme: previewState.theme, brandColor: previewState.palette.brandColor, hideEventTypeDetails: previewState.hideEventTypeDetails, @@ -189,7 +189,7 @@ const getEmbedTypeSpecificString = ({ }; } else { uiInstructionStringArg = { - apiName: getApiName({ namespace, mainApiName: "Cal" }), + apiName: getApiNameForVanillaJsSnippet({ namespace, mainApiName: "Cal" }), theme: previewState.theme, brandColor: previewState.palette.brandColor, hideEventTypeDetails: previewState.hideEventTypeDetails, diff --git a/packages/features/embed/lib/getApiName.test.tsx b/packages/features/embed/lib/getApiName.test.tsx new file mode 100644 index 0000000000..2306c89b90 --- /dev/null +++ b/packages/features/embed/lib/getApiName.test.tsx @@ -0,0 +1,28 @@ +import { describe, it, expect } from "vitest"; + +import { getApiNameWithNamespace } from "./getApiName"; + +describe("getApiNameWithNamespace", () => { + describe("when namespace is a valid variable", () => { + it("should return the correct API name with namespace for a non-hyphenated namespace", () => { + const result = getApiNameWithNamespace({ namespace: "first", mainApiName: "cal" }); + expect(result).toBe("cal.ns.first"); + }); + it("should return the correct API name with namespace for a$b", () => { + const result = getApiNameWithNamespace({ namespace: "a$b", mainApiName: "cal" }); + expect(result).toBe("cal.ns.a$b"); + }); + }); + + describe("when namespace is not a valid variable", () => { + it("should return the correct API name with namespace for a hyphenated namespace", () => { + const result = getApiNameWithNamespace({ namespace: "first-one", mainApiName: "cal" }); + expect(result).toBe('cal.ns["first-one"]'); + }); + + it("should return the correct API name with namespace for a&n", () => { + const result = getApiNameWithNamespace({ namespace: "a&n", mainApiName: "cal" }); + expect(result).toBe('cal.ns["a&n"]'); + }); + }); +}); diff --git a/packages/features/embed/lib/getApiName.tsx b/packages/features/embed/lib/getApiName.tsx index 17a0013ab4..5c9e891570 100644 --- a/packages/features/embed/lib/getApiName.tsx +++ b/packages/features/embed/lib/getApiName.tsx @@ -1,14 +1,29 @@ -export function getApiName({ +export function getApiNameWithNamespace({ namespace, - mainApiName = "Cal", + mainApiName, }: { - namespace: string | null; - mainApiName?: string; + namespace: string; + mainApiName: string; }) { - if (!namespace) { - return mainApiName; - } const isAValidVariableName = /^[a-zA-Z_$][a-zA-Z_$0-9]*$/.test(namespace); // Try to use dot notation if possible because it's more readable otherwise use bracket notation return isAValidVariableName ? `${mainApiName}.ns.${namespace}` : `${mainApiName}.ns["${namespace}"]`; } + +function getApiNameWithoutNamespace({ mainApiName }: { mainApiName: string }) { + return mainApiName; +} + +export function getApiNameForReactSnippet({ mainApiName }: { mainApiName: string }) { + return getApiNameWithoutNamespace({ mainApiName }); +} + +export function getApiNameForVanillaJsSnippet({ + namespace, + mainApiName, +}: { + namespace: string; + mainApiName: string; +}) { + return getApiNameWithNamespace({ mainApiName, namespace }); +}