Revert "Fix: Embed Fixes, UI configuration PRO Only, Tests (#2341)"
This reverts commit 5138c676b1
.
pull/2392/head
parent
67e1a35650
commit
1bc3272149
|
@ -11,11 +11,11 @@ node_modules
|
|||
# testing
|
||||
coverage
|
||||
/test-results/
|
||||
**/playwright/videos
|
||||
**/playwright/screenshots
|
||||
**/playwright/artifacts
|
||||
**/playwright/results
|
||||
**/playwright/reports/*
|
||||
playwright/videos
|
||||
playwright/screenshots
|
||||
playwright/artifacts
|
||||
playwright/results
|
||||
playwright/reports/*
|
||||
|
||||
# next.js
|
||||
.next/
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
title: Embed Snippet
|
||||
---
|
||||
|
||||
# Embed Snippet
|
||||
|
||||
The Embed Snippet allows your website visitors to book a meeting with you directly from your website. It works by you installing a small Javascript Snippet to your website.
|
||||
[Mention possiblity of installation through tag managers as well]
|
|
@ -19,7 +19,6 @@ import { FormattedNumber, IntlProvider } from "react-intl";
|
|||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { timeZone } from "@lib/clock";
|
||||
import { BASE_URL } from "@lib/config/constants";
|
||||
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import useTheme from "@lib/hooks/useTheme";
|
||||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||
|
@ -42,13 +41,13 @@ dayjs.extend(customParseFormat);
|
|||
|
||||
type Props = AvailabilityTeamPageProps | AvailabilityPageProps;
|
||||
|
||||
const AvailabilityPage = ({ profile, plan, eventType, workingHours, previousPage }: Props) => {
|
||||
const AvailabilityPage = ({ profile, eventType, workingHours, previousPage }: Props) => {
|
||||
const router = useRouter();
|
||||
const { rescheduleUid } = router.query;
|
||||
const { isReady, Theme } = useTheme(profile.theme);
|
||||
const { t } = useLocale();
|
||||
const { contracts } = useContracts();
|
||||
useExposePlanGlobally(plan);
|
||||
|
||||
useEffect(() => {
|
||||
if (eventType.metadata.smartContractAddress) {
|
||||
const eventOwner = eventType.users[0];
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
import { useEffect } from "react";
|
||||
|
||||
import { UserPlan } from "@calcom/prisma/client";
|
||||
|
||||
export function useExposePlanGlobally(plan: UserPlan) {
|
||||
// Don't wait for component to mount. Do it ASAP. Delaying it would delay UI Configuration.
|
||||
if (typeof window !== "undefined") {
|
||||
// This variable is used by embed-iframe to determine if we should allow UI configuration
|
||||
window.CalComPlan = plan;
|
||||
}
|
||||
}
|
|
@ -4,14 +4,13 @@ import { GetServerSidePropsContext } from "next";
|
|||
import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import { JSONObject } from "superjson/dist/types";
|
||||
|
||||
import { sdkActionManager, useEmbedStyles } from "@calcom/embed-core";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
||||
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
|
||||
import useTheme from "@lib/hooks/useTheme";
|
||||
import prisma from "@lib/prisma";
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
@ -36,7 +35,7 @@ export default function User(props: inferSSRProps<typeof getServerSideProps>) {
|
|||
const eventTypeListItemEmbedStyles = useEmbedStyles("eventTypeListItem");
|
||||
const query = { ...router.query };
|
||||
delete query.user; // So it doesn't display in the Link (and make tests fail)
|
||||
useExposePlanGlobally("PRO");
|
||||
|
||||
const nameOrUsername = user.name || user.username || "";
|
||||
const [evtsToVerify, setEvtsToVerify] = useState<EvtsToVerify>({});
|
||||
return (
|
||||
|
|
|
@ -226,7 +226,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
brandColor: user.brandColor,
|
||||
darkBrandColor: user.darkBrandColor,
|
||||
},
|
||||
plan: user.plan,
|
||||
date: dateParam,
|
||||
eventType: eventTypeObject,
|
||||
workingHours,
|
||||
|
|
|
@ -7,7 +7,6 @@ import React from "react";
|
|||
import Button from "@calcom/ui/Button";
|
||||
|
||||
import { getPlaceholderAvatar } from "@lib/getPlaceholderAvatar";
|
||||
import { useExposePlanGlobally } from "@lib/hooks/useExposePlanGlobally";
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
import useTheme from "@lib/hooks/useTheme";
|
||||
import { useToggleQuery } from "@lib/hooks/useToggleQuery";
|
||||
|
@ -28,7 +27,7 @@ function TeamPage({ team }: TeamPageProps) {
|
|||
const { isReady, Theme } = useTheme();
|
||||
const showMembers = useToggleQuery("members");
|
||||
const { t } = useLocale();
|
||||
useExposePlanGlobally("PRO");
|
||||
|
||||
const eventTypes = (
|
||||
<ul className="space-y-3">
|
||||
{team.eventTypes.map((type) => (
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { GetServerSidePropsContext } from "next";
|
||||
import { JSONObject } from "superjson/dist/types";
|
||||
|
||||
import { UserPlan } from "@calcom/prisma/client";
|
||||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { getWorkingHours } from "@lib/availability";
|
||||
import prisma from "@lib/prisma";
|
||||
|
@ -112,8 +110,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
|
||||
return {
|
||||
props: {
|
||||
// Team is always pro
|
||||
plan: "PRO" as UserPlan,
|
||||
profile: {
|
||||
name: team.name || team.slug,
|
||||
slug: team.slug,
|
||||
|
|
|
@ -14,50 +14,49 @@ See [index.html](index.html) to understand how it can be used.
|
|||
- `notes`
|
||||
- `guests`
|
||||
|
||||
## How to use embed on any webpage no matter what framework
|
||||
## How to use embed on any webpage no matter what framework.
|
||||
|
||||
- _Step-1._ Install the snippet
|
||||
|
||||
```javascript
|
||||
(function(C, A, L) {
|
||||
let p = function(a, ar) {
|
||||
a.q.push(ar);
|
||||
};
|
||||
```javascript
|
||||
(function (C, A, L) {
|
||||
let d = C.document;
|
||||
C.Cal = C.Cal || function() {
|
||||
let cal = C.Cal;
|
||||
let ar = arguments;
|
||||
if (!cal.loaded) {
|
||||
cal.ns = {};
|
||||
cal.q = cal.q || [];
|
||||
d.head.appendChild(d.createElement("script")).src = A;
|
||||
cal.loaded = true;
|
||||
}
|
||||
if (ar[0] === L) {
|
||||
const api = function() {
|
||||
p(api, arguments);
|
||||
};
|
||||
const namespace = ar[1];
|
||||
api.q = api.q || [];
|
||||
typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar);
|
||||
return;
|
||||
}
|
||||
p(cal, ar);
|
||||
};
|
||||
C.Cal =
|
||||
C.Cal ||
|
||||
function () {
|
||||
let cal = C.Cal;
|
||||
let ar = arguments;
|
||||
if (!cal.loaded) {
|
||||
cal.ns = {};
|
||||
cal.q = cal.q || [];
|
||||
d.head.appendChild(d.createElement("script")).src = A;
|
||||
cal.loaded = true;
|
||||
}
|
||||
if (ar[0] === L) {
|
||||
const api = function () {
|
||||
api.q.push(arguments);
|
||||
};
|
||||
const namespace = arguments[1];
|
||||
api.q = api.q || [];
|
||||
namespace ? (cal.ns[namespace] = api) : null;
|
||||
return;
|
||||
}
|
||||
cal.q.push(ar);
|
||||
};
|
||||
})(window, "https://cal.com/embed.js", "init");
|
||||
```
|
||||
|
||||
- _Step-2_. Give `init` instruction to it. It creates a queue so that even without embed.js being fetched, you can give instructions to embed.
|
||||
|
||||
```javascript
|
||||
Cal("init) // Creates default instance. Give instruction to it as Cal("instruction")
|
||||
```
|
||||
```javascript
|
||||
Cal("init) // Creates default instance. Give instruction to it as Cal("instruction")
|
||||
```
|
||||
|
||||
**Optionally** if you want to install another instance of embed you can do
|
||||
**Optionally** if you want to install another instance of embed you can do
|
||||
|
||||
```javascript
|
||||
Cal("init", "NAME_YOUR_OTHER_INSTANCE"); // Creates a named instance. Give instructions to it as Cal.ns.NAME_YOUR_OTHER_INSTANCE("instruction")
|
||||
```
|
||||
```javascript
|
||||
Cal("init", "NAME_YOUR_OTHER_INSTANCE"); // Creates a named instance. Give instructions to it as Cal.ns.NAME_YOUR_OTHER_INSTANCE("instruction")
|
||||
```
|
||||
|
||||
- Step-1 and Step-2 must be followed in same order. After that you can give various instructions to embed as you like.
|
||||
|
||||
|
@ -93,53 +92,4 @@ yarn dev
|
|||
yarn build
|
||||
```
|
||||
|
||||
Make `dist/embed.umd.js` servable on URL <http://cal.com/embed.js>
|
||||
|
||||
## Upcoming Improvements
|
||||
|
||||
- Unsupported Browsers and versions. Documenting them and gracefully handling that.
|
||||
|
||||
- Accessibility and UI/UX Issues
|
||||
- Loader on ModalBox/popup
|
||||
- If website owner links the booking page directly for an event, should the user be able to go to events-listing page using back button ?
|
||||
|
||||
- Bundling Related
|
||||
- Minify CSS in embed.js
|
||||
|
||||
- Debuggability
|
||||
- Send log messages from iframe to parent so that all logs can exist in a single queue forming a timeline.
|
||||
- user should be able to use "on" instruction to understand what's going on in the system
|
||||
- Error Tracking for embed.js
|
||||
- Know where exactly it’s failing if it does.
|
||||
|
||||
- Improved Demo
|
||||
- Seeding might be done for team event so that such an example is also available readily in index.html
|
||||
|
||||
- Dev Experience/Ease of Installation
|
||||
- Do we need a one liner(like `window.dataLayer.push`) to inform SDK of something even if snippet is not yet on the page but would be there e.g. through GTM it would come late on the page ?
|
||||
- Might be better to pass all configuration using a single base64encoded query param to booking page.
|
||||
- Embed Code Generator
|
||||
|
||||
- UI Config Features
|
||||
- Theme switch dynamically - If user switches the theme on website, he should be able to do it on embed.
|
||||
- Text Color
|
||||
- Brand color
|
||||
- At some places Text is colored by using the color specific tailwind class. e.g. `text-gray-400` is the color of disabled date. He has 2 options, If user wants to customize that
|
||||
- He can go and override the color on the class which doesn’t make sense
|
||||
- He can identify the element and change the color by directly adding style, which might cause consistency issues if certain elements are missed.
|
||||
- Challenges
|
||||
- How would the user add on hover styles just using style attribute ?
|
||||
- React Component
|
||||
- `onClick` support with preloading
|
||||
|
||||
## Pending Documentation
|
||||
|
||||
- READMEs
|
||||
- How to make a new element configurable using UI instruction ?
|
||||
- Why do we NOT want to provide completely flexible CSS customization by adding whatever CSS user wants. ?
|
||||
|
||||
- docs.cal.com
|
||||
- A complete document on how to use embed
|
||||
|
||||
- app.cal.com
|
||||
- Get Embed code for each event-type
|
||||
Make `dist/embed.umd.js` servable on URL http://cal.com/embed.js
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
</script>
|
||||
<script>
|
||||
(function (C, A, L) {
|
||||
let p = function (a, ar) {
|
||||
a.q.push(ar);
|
||||
};
|
||||
let d = C.document;
|
||||
C.Cal =
|
||||
C.Cal ||
|
||||
|
@ -25,18 +22,25 @@
|
|||
}
|
||||
if (ar[0] === L) {
|
||||
const api = function () {
|
||||
p(api, arguments);
|
||||
api.q.push(arguments);
|
||||
};
|
||||
const namespace = ar[1];
|
||||
const namespace = arguments[1];
|
||||
api.q = api.q || [];
|
||||
typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar);
|
||||
return;
|
||||
namespace ? (cal.ns[namespace] = api) : null;
|
||||
}
|
||||
p(cal, ar);
|
||||
cal.q.push(ar);
|
||||
};
|
||||
})(window, "//localhost:3002/dist/embed.umd.js", "init");
|
||||
</script>
|
||||
<script>
|
||||
Cal("init");
|
||||
|
||||
// Create a namespace "second". It can be accessed as Cal.ns.second with the exact same API as Cal
|
||||
Cal("init", "second");
|
||||
|
||||
// Create a namespace "third". It can be accessed as Cal.ns.second with the exact same API as Cal
|
||||
Cal("init", "third");
|
||||
</script>
|
||||
<style>
|
||||
.debug {
|
||||
/* border: 1px solid black; */
|
||||
|
@ -50,7 +54,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<h3>This page has a non responsive version accessible <a href="?nonResponsive">here</a></h3>
|
||||
<h3>Pre-render test page available at <a href="?only=prerender-test">here</a></h3>
|
||||
<h3>Pre-render test page available at <a href="?prerender-test">here</a></h3>
|
||||
<div>
|
||||
<button data-cal-link="free">Book with Free User</button>
|
||||
<div>
|
||||
|
@ -63,15 +67,13 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="namespaces-test">
|
||||
<div class="debug" id="cal-booking-place-default">
|
||||
<h2>
|
||||
Default Namespace(Cal)<i>[Dark Theme][inline][Guests(janedoe@gmail.com and test@gmail.com)]</i>
|
||||
</h2>
|
||||
<div class="debug">
|
||||
<h2>Default Namespace(Cal)<i>[Black Theme][Guests(janedoe@gmail.com and test@gmail.com)]</i></h2>
|
||||
<div>
|
||||
<i><a href="?only=ns:default">Test in Zen Mode</a></i>
|
||||
</div>
|
||||
<i class="last-action"> You would see last Booking page action in my place </i>
|
||||
<div style="max-height: 30vh; overflow: scroll" class="place">
|
||||
<i id="booking-status-"> You would see last Booking page action in my place </i>
|
||||
<div id="cal-booking-place-" style="max-height: 30vh; overflow: scroll">
|
||||
<div>
|
||||
if you render booking embed in me, I would not let it be more than 30vh in height. So you would
|
||||
have to scroll to see the entire content
|
||||
|
@ -79,15 +81,15 @@
|
|||
<div class="loader" id="cal-booking-loader-">Loading .....</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="debug" id="cal-booking-place-second">
|
||||
<h2>Namespace "second"(Cal.ns.second)[Custom Styling][inline]</h2>
|
||||
<div class="debug">
|
||||
<h2>Namespace "second"(Cal.ns.second)[Custom Styling]</h2>
|
||||
<div>
|
||||
<i><a href="?only=ns:second">Test in Zen Mode</a></i>
|
||||
</div>
|
||||
<i class="last-action">
|
||||
<i id="booking-status-second">
|
||||
<i>You would see last Booking page action in my place</i>
|
||||
</i>
|
||||
<div class="place">
|
||||
<div id="cal-booking-place-second">
|
||||
<div>If you render booking embed in me, I won't restrict you. The entire page is yours.</div>
|
||||
<button
|
||||
onclick="(function () {Cal.ns.second('ui', {styles:{eventTypeListItem:{backgroundColor:'blue'}}})})()">
|
||||
|
@ -100,29 +102,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug" id="cal-booking-place-third">
|
||||
<h2>Namespace "third"(Cal.ns.third)[inline]</h2>
|
||||
<div class="debug">
|
||||
<h2>Namespace "third"(Cal.ns.third)</h2>
|
||||
<div>
|
||||
<i><a href="?only=ns:third">Test in Zen Mode</a></i>
|
||||
</div>
|
||||
<i class="last-action">
|
||||
<i id="booking-status-third">
|
||||
<i>You would see last Booking page action in my place</i>
|
||||
</i>
|
||||
<div style="width: 30%" class="place">
|
||||
<div>If you render booking embed in me, I would not let you be more than 30% wide</div>
|
||||
<div class="loader" id="cal-booking-loader-third">Loading .....</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug" id="cal-booking-place-fourth">
|
||||
<h2>Namespace "fourth"(Cal.ns.fourth)[Team Event Test][inline]</h2>
|
||||
<div>
|
||||
<i><a href="?only=ns:fourth">Test in Zen Mode</a></i>
|
||||
</div>
|
||||
<i class="last-action">
|
||||
<i>You would see last Booking page action in my place</i>
|
||||
</i>
|
||||
<div style="width: 30%" class="place">
|
||||
<div id="cal-booking-place-third" style="width: 30%">
|
||||
<div>If you render booking embed in me, I would not let you be more than 30% wide</div>
|
||||
<div class="loader" id="cal-booking-loader-third">Loading .....</div>
|
||||
</div>
|
||||
|
@ -130,152 +118,108 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
const callback = function (e) {
|
||||
const detail = e.detail;
|
||||
const namespace = detail.namespace || "default";
|
||||
|
||||
if (detail.type === "linkReady") {
|
||||
document.querySelector(`#cal-booking-place-${namespace} .loader`).remove();
|
||||
}
|
||||
|
||||
document.querySelector(`#cal-booking-place-${namespace} .last-action`).innerHTML = JSON.stringify(
|
||||
e.detail
|
||||
);
|
||||
};
|
||||
|
||||
const searchParams = new URL(document.URL).searchParams;
|
||||
const only = searchParams.get("only");
|
||||
if (!only || only === "ns:default") {
|
||||
Cal("init", {
|
||||
debug: 1,
|
||||
origin: "http://localhost:3000",
|
||||
});
|
||||
|
||||
Cal("inline", {
|
||||
elementOrSelector: "#cal-booking-place-default .place",
|
||||
calLink: "pro?case=1",
|
||||
config: {
|
||||
name: "John Doe",
|
||||
email: "johndoe@gmail.com",
|
||||
notes: "Test Meeting",
|
||||
guests: ["janedoe@gmail.com", "test@gmail.com"],
|
||||
theme: "dark",
|
||||
},
|
||||
});
|
||||
Cal("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
}
|
||||
if (!only || only === "ns:second") {
|
||||
// Create a namespace "second". It can be accessed as Cal.ns.second with the exact same API as Cal
|
||||
Cal("init", "second", {
|
||||
debug: 1,
|
||||
origin: "http://localhost:3000",
|
||||
});
|
||||
|
||||
// Bulk API is supported - Keep all configuration at one place.
|
||||
Cal.ns.second(
|
||||
[
|
||||
"inline",
|
||||
{
|
||||
elementOrSelector: "#cal-booking-place-second .place",
|
||||
calLink: "pro?case=2",
|
||||
// In prerender-test, we would want to test just the prerender case and nothing else.
|
||||
if (searchParams.get("prerender-test") === null) {
|
||||
if (!only || only === "ns:default") {
|
||||
Cal("inline", {
|
||||
elementOrSelector: "#cal-booking-place-",
|
||||
calLink: "pro?case=1",
|
||||
config: {
|
||||
name: "John Doe",
|
||||
email: "johndoe@gmail.com",
|
||||
notes: "Test Meeting",
|
||||
guests: ["janedoe@gmail.com", "test@gmail.com"],
|
||||
theme: "dark",
|
||||
},
|
||||
],
|
||||
[
|
||||
"ui",
|
||||
{
|
||||
styles: {
|
||||
body: {
|
||||
background: "white",
|
||||
},
|
||||
eventTypeListItem: {
|
||||
backgroundColor: "#D3D3D3",
|
||||
},
|
||||
enabledDateButton: {
|
||||
backgroundColor: "#D3D3D3",
|
||||
},
|
||||
disabledDateButton: {
|
||||
backgroundColor: "lightslategray",
|
||||
});
|
||||
}
|
||||
if (!only || only === "ns:second") {
|
||||
// Bulk API is supported - Keep all configuration at one place.
|
||||
Cal.ns.second(
|
||||
[
|
||||
"inline",
|
||||
{
|
||||
elementOrSelector: "#cal-booking-place-second",
|
||||
calLink: "pro?case=2",
|
||||
},
|
||||
],
|
||||
[
|
||||
"ui",
|
||||
{
|
||||
styles: {
|
||||
body: {
|
||||
background: "white",
|
||||
},
|
||||
eventTypeListItem: {
|
||||
backgroundColor: "#D3D3D3",
|
||||
},
|
||||
enabledDateButton: {
|
||||
backgroundColor: "#D3D3D3",
|
||||
},
|
||||
disabledDateButton: {
|
||||
backgroundColor: "lightslategray",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
Cal.ns.second("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if (!only || only === "ns:third") {
|
||||
// Create a namespace "third". It can be accessed as Cal.ns.second with the exact same API as Cal
|
||||
Cal("init", "third", {
|
||||
debug: 1,
|
||||
origin: "http://localhost:3000",
|
||||
});
|
||||
Cal.ns.third(
|
||||
[
|
||||
"inline",
|
||||
{
|
||||
elementOrSelector: "#cal-booking-place-third .place",
|
||||
calLink: "pro/30min",
|
||||
},
|
||||
],
|
||||
[
|
||||
"ui",
|
||||
{
|
||||
styles: {
|
||||
body: {
|
||||
background: "white",
|
||||
if (!only || only === "ns:third") {
|
||||
Cal.ns.third(
|
||||
[
|
||||
"inline",
|
||||
{
|
||||
elementOrSelector: "#cal-booking-place-third",
|
||||
calLink: "pro?case=3",
|
||||
},
|
||||
],
|
||||
[
|
||||
"ui",
|
||||
{
|
||||
styles: {
|
||||
body: {
|
||||
background: "white",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
Cal.ns.third("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
document.getElementById("namespaces-test").style.display = "none";
|
||||
}
|
||||
|
||||
if (!only || only === "ns:fourth") {
|
||||
Cal("init", "fourth", {
|
||||
debug: 1,
|
||||
origin: "http://localhost:3000",
|
||||
});
|
||||
Cal.ns.fourth(
|
||||
[
|
||||
"inline",
|
||||
{
|
||||
elementOrSelector: "#cal-booking-place-fourth .place",
|
||||
calLink: "team/test-team",
|
||||
},
|
||||
],
|
||||
[
|
||||
"ui",
|
||||
{
|
||||
styles: {
|
||||
body: {
|
||||
background: "white",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
);
|
||||
Cal.ns.fourth("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
if (!only || only === "prerender-test") {
|
||||
Cal("preload", {
|
||||
calLink: "free",
|
||||
});
|
||||
}
|
||||
Cal("preload", {
|
||||
calLink: "free",
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
const callback = function (e) {
|
||||
const detail = e.detail;
|
||||
const namespace = detail.namespace;
|
||||
|
||||
if (detail.type === "linkReady") {
|
||||
document.getElementById("cal-booking-loader-" + namespace).remove();
|
||||
}
|
||||
|
||||
document.getElementById(`booking-status-${namespace}`).innerHTML = JSON.stringify(e.detail);
|
||||
};
|
||||
|
||||
Cal("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
Cal.ns.second("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
Cal.ns.third("on", {
|
||||
action: "*",
|
||||
callback,
|
||||
});
|
||||
</script>
|
||||
<script></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -5,12 +5,10 @@
|
|||
"main": "./index.ts",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"build:cal": "NEXT_PUBLIC_WEBSITE_URL='https://cal.com' yarn build",
|
||||
"vite": "vite",
|
||||
"dev": "run-p 'build --watch' 'vite --port 3002 --strict-port --open'",
|
||||
"type-check": "tsc --pretty --noEmit",
|
||||
"lint": "eslint --ext .ts,.js src",
|
||||
"test-playwright": "yarn playwright test --config=playwright/config/playwright.config.ts"
|
||||
"lint": "eslint --ext .ts,.js src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^2.8.6",
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
async function globalSetup(/* config: FullConfig */) {}
|
||||
|
||||
export default globalSetup;
|
|
@ -1,119 +0,0 @@
|
|||
import { PlaywrightTestConfig, Frame, devices, expect } from "@playwright/test";
|
||||
import * as path from "path";
|
||||
|
||||
const outputDir = path.join("../results");
|
||||
const testDir = path.join("../tests");
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 1,
|
||||
workers: 1,
|
||||
timeout: 60_000,
|
||||
reporter: [
|
||||
[process.env.CI ? "github" : "list"],
|
||||
[
|
||||
"html",
|
||||
{ outputFolder: path.join(__dirname, "..", "reports", "playwright-html-report"), open: "never" },
|
||||
],
|
||||
["junit", { outputFile: path.join(__dirname, "..", "reports", "results.xml") }],
|
||||
],
|
||||
globalSetup: require.resolve("./globalSetup"),
|
||||
outputDir,
|
||||
webServer: {
|
||||
// Start App Server manually - Can't be handled here. See https://github.com/microsoft/playwright/issues/8206
|
||||
command: "yarn workspace @calcom/embed-core dev",
|
||||
port: 3002,
|
||||
timeout: 60_000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
use: {
|
||||
baseURL: "http://localhost:3002",
|
||||
locale: "en-US",
|
||||
trace: "retain-on-failure",
|
||||
headless: !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
testDir,
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
/* {
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
}, */
|
||||
],
|
||||
};
|
||||
export type ExpectedUrlDetails = {
|
||||
searchParams?: Record<string, string | string[]>;
|
||||
pathname?: string;
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
declare global {
|
||||
namespace PlaywrightTest {
|
||||
//FIXME: how to restrict it to Frame only
|
||||
interface Matchers<R> {
|
||||
toBeEmbedCalLink(expectedUrlDetails?: ExpectedUrlDetails): R;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect.extend({
|
||||
async toBeEmbedCalLink(iframe: Frame, expectedUrlDetails: ExpectedUrlDetails = {}) {
|
||||
if (!iframe || !iframe.url) {
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Expected to provide an iframe, got ${iframe}`,
|
||||
};
|
||||
}
|
||||
|
||||
const u = new URL(iframe.url());
|
||||
const frameElement = await iframe.frameElement();
|
||||
|
||||
if (!(await frameElement.isVisible())) {
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Expected iframe to be visible`,
|
||||
};
|
||||
}
|
||||
const pathname = u.pathname;
|
||||
const expectedPathname = expectedUrlDetails.pathname;
|
||||
if (expectedPathname && expectedPathname !== pathname) {
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Expected pathname to be ${expectedPathname} but got ${pathname}`,
|
||||
};
|
||||
}
|
||||
|
||||
const origin = u.origin;
|
||||
const expectedOrigin = expectedUrlDetails.origin;
|
||||
if (expectedOrigin && expectedOrigin !== origin) {
|
||||
return {
|
||||
pass: false,
|
||||
message: () => `Expected origin to be ${expectedOrigin} but got ${origin}`,
|
||||
};
|
||||
}
|
||||
|
||||
const searchParams = u.searchParams;
|
||||
const expectedSearchParams = expectedUrlDetails.searchParams || {};
|
||||
for (let [expectedKey, expectedValue] of Object.entries(expectedSearchParams)) {
|
||||
const value = searchParams.get(expectedKey);
|
||||
if (value !== expectedValue) {
|
||||
return {
|
||||
message: () => `${expectedKey} should have value ${expectedValue} but got value ${value}`,
|
||||
pass: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
pass: true,
|
||||
message: () => `passed`,
|
||||
};
|
||||
},
|
||||
});
|
||||
export default config;
|
|
@ -1,3 +0,0 @@
|
|||
import { test as base } from "@playwright/test";
|
||||
|
||||
export const test = base.extend({});
|
|
@ -1,21 +0,0 @@
|
|||
import { Page } from "@playwright/test";
|
||||
|
||||
export function todo(title: string) {
|
||||
test.skip(title, () => {});
|
||||
}
|
||||
|
||||
export const getEmbedIframe = async ({ page, pathname }: { page: Page; pathname: string }) => {
|
||||
// FIXME: Need to wait for the iframe to be properly added to shadow dom. There should be a no time boundation way to do it.
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, 1000);
|
||||
});
|
||||
let embedIframe = page.frame("cal-embed");
|
||||
if (!embedIframe) {
|
||||
return null;
|
||||
}
|
||||
const u = new URL(embedIframe.url());
|
||||
if (u.pathname === pathname) {
|
||||
return embedIframe;
|
||||
}
|
||||
return null;
|
||||
};
|
|
@ -1,17 +0,0 @@
|
|||
import { expect } from "@playwright/test";
|
||||
|
||||
import { test } from "../fixtures/fixtures";
|
||||
import { todo, getEmbedIframe } from "../lib/testUtils";
|
||||
|
||||
test("should open embed iframe on click", async ({ page }) => {
|
||||
await page.goto("/?only=prerender-test");
|
||||
let embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
expect(embedIframe).toBeFalsy();
|
||||
|
||||
await page.click('[data-cal-link="free"]');
|
||||
|
||||
embedIframe = await getEmbedIframe({ page, pathname: "/free" });
|
||||
expect(embedIframe).toBeEmbedCalLink({
|
||||
pathname: "/free",
|
||||
});
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import { expect, Frame } from "@playwright/test";
|
||||
|
||||
import { test } from "../fixtures/fixtures";
|
||||
import { todo } from "../lib/testUtils";
|
||||
|
||||
test("Inline Iframe - Configured with Dark Theme", async ({ page }) => {
|
||||
await page.goto("/?only=ns:default");
|
||||
const embedIframe = page.frame({ url: /.*pro.*/ });
|
||||
expect(embedIframe).toBeEmbedCalLink({
|
||||
pathname: "/pro",
|
||||
searchParams: {
|
||||
theme: "dark",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
todo(
|
||||
"Ensure that on all pages - [user], [user]/[type], team/[slug], team/[slug]/book, UI styling works if these pages are directly linked in embed"
|
||||
);
|
||||
|
||||
todo("Check that UI Configuration doesn't work for Free Plan");
|
|
@ -2,51 +2,6 @@ import { useState, useEffect, CSSProperties } from "react";
|
|||
|
||||
import { sdkActionManager } from "./sdk-event";
|
||||
|
||||
let isSafariBrowser = false;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
isSafariBrowser = ua.includes("safari") && !ua.includes("chrome");
|
||||
if (isSafariBrowser) {
|
||||
log("Safari Detected: Using setTimeout instead of rAF");
|
||||
}
|
||||
}
|
||||
|
||||
function keepRunningAsap(fn: (...arg: any) => void) {
|
||||
if (isSafariBrowser) {
|
||||
// https://adpiler.com/blog/the-full-solution-why-do-animations-run-slower-in-safari/
|
||||
return setTimeout(fn, 50);
|
||||
}
|
||||
return requestAnimationFrame(fn);
|
||||
}
|
||||
declare global {
|
||||
interface Window {
|
||||
CalEmbed: {
|
||||
__logQueue?: any[];
|
||||
};
|
||||
CalComPlan: string;
|
||||
}
|
||||
}
|
||||
|
||||
function log(...args: any[]) {
|
||||
let namespace;
|
||||
if (typeof window !== "undefined") {
|
||||
const searchParams = new URL(document.URL).searchParams;
|
||||
namespace = typeof searchParams.get("embed") !== "undefined" ? "" : "_unknown_";
|
||||
//TODO: Send postMessage to parent to get all log messages in the same queue.
|
||||
window.CalEmbed = window.CalEmbed || {};
|
||||
const logQueue = (window.CalEmbed.__logQueue = window.CalEmbed.__logQueue || []);
|
||||
args.push({
|
||||
ns: namespace,
|
||||
});
|
||||
args.unshift("CAL:");
|
||||
logQueue.push(args);
|
||||
if (searchParams.get("debug")) {
|
||||
console.log(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only allow certain styles to be modified so that when we make any changes to HTML, we know what all embed styles might be impacted.
|
||||
// Keep this list to minimum, only adding those styles which are really needed.
|
||||
interface EmbedStyles {
|
||||
|
@ -111,23 +66,15 @@ export const useEmbedStyles = (elementName: ElementName) => {
|
|||
return styles[elementName] || {};
|
||||
};
|
||||
|
||||
function unhideBody() {
|
||||
document.body.style.display = "block";
|
||||
}
|
||||
// If you add a method here, give type safety to parent manually by adding it to embed.ts. Look for "parentKnowsIframeReady" in it
|
||||
export const methods = {
|
||||
ui: function style(uiConfig: UiConfig) {
|
||||
// TODO: Create automatic logger for all methods. Useful for debugging.
|
||||
log("Method: ui called", uiConfig);
|
||||
if (window.CalComPlan && window.CalComPlan !== "PRO") {
|
||||
log(`Upgrade to PRO for "ui" instruction to work`, window.CalComPlan);
|
||||
return;
|
||||
}
|
||||
console.log("Method: ui called", uiConfig);
|
||||
const stylesConfig = uiConfig.styles;
|
||||
|
||||
// In case where parent gives instructions before CalComPlan is set.
|
||||
// This is easily possible as React takes time to initialize and render components where this variable is set.
|
||||
if (!window.CalComPlan) {
|
||||
// In case where parent gives instructions before setEmbedStyles is set.
|
||||
if (!setEmbedStyles) {
|
||||
return requestAnimationFrame(() => {
|
||||
style(uiConfig);
|
||||
});
|
||||
|
@ -141,8 +88,7 @@ export const methods = {
|
|||
setEmbedStyles(stylesConfig);
|
||||
},
|
||||
parentKnowsIframeReady: () => {
|
||||
log("Method: `parentKnowsIframeReady` called");
|
||||
unhideBody();
|
||||
document.body.style.display = "block";
|
||||
sdkActionManager?.fire("linkReady", {});
|
||||
},
|
||||
};
|
||||
|
@ -158,41 +104,18 @@ const messageParent = (data: any) => {
|
|||
};
|
||||
|
||||
function keepParentInformedAboutDimensionChanges() {
|
||||
console.log("keepParentInformedAboutDimensionChanges executed");
|
||||
|
||||
let knownIframeHeight: Number | null = null;
|
||||
let knownHiddenHeight: Number | null = null;
|
||||
let numDimensionChanges = 0;
|
||||
let isFirstTime = true;
|
||||
let isWindowLoadComplete = false;
|
||||
keepRunningAsap(function informAboutScroll() {
|
||||
if (document.readyState !== "complete") {
|
||||
// Wait for window to load to correctly calculate the initial scroll height.
|
||||
keepRunningAsap(informAboutScroll);
|
||||
return;
|
||||
}
|
||||
if (!isWindowLoadComplete) {
|
||||
// On Safari, even though document.readyState is complete, still the page is not rendered and we can't compute documentElement.scrollHeight correctly
|
||||
// Postponing to just next cycle allow us to fix this.
|
||||
setTimeout(() => {
|
||||
isWindowLoadComplete = true;
|
||||
informAboutScroll();
|
||||
}, 10);
|
||||
return;
|
||||
}
|
||||
const documentScrollHeight = document.documentElement.scrollHeight;
|
||||
const contentHeight = document.documentElement.offsetHeight;
|
||||
// During first render let iframe tell parent that how much is the expected height to avoid scroll.
|
||||
// Parent would set the same value as the height of iframe which would prevent scroll.
|
||||
// On subsequent renders, consider html height as the height of the iframe. If we don't do this, then if iframe get's bigger in height, it would never shrink
|
||||
let iframeHeight = isFirstTime ? documentScrollHeight : contentHeight;
|
||||
isFirstTime = false;
|
||||
requestAnimationFrame(function informAboutScroll() {
|
||||
// Because of scroll="no", this much is hidden from the user.
|
||||
const hiddenHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||
// TODO: Handle width as well.
|
||||
if (knownIframeHeight !== iframeHeight) {
|
||||
knownIframeHeight = iframeHeight;
|
||||
if (knownHiddenHeight !== hiddenHeight) {
|
||||
knownHiddenHeight = hiddenHeight;
|
||||
numDimensionChanges++;
|
||||
// FIXME: This event shouldn't be subscribable by the user. Only by the SDK.
|
||||
sdkActionManager?.fire("dimension-changed", {
|
||||
iframeHeight,
|
||||
hiddenHeight,
|
||||
});
|
||||
}
|
||||
// Parent Counterpart would change the dimension of iframe and thus page's dimension would be impacted which is recursive.
|
||||
|
@ -202,39 +125,28 @@ function keepParentInformedAboutDimensionChanges() {
|
|||
console.warn("Too many dimension changes detected.");
|
||||
return;
|
||||
}
|
||||
keepRunningAsap(informAboutScroll);
|
||||
requestAnimationFrame(informAboutScroll);
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
const url = new URL(document.URL);
|
||||
if (url.searchParams.get("prerender") !== "true" && typeof url.searchParams.get("embed") !== "undefined") {
|
||||
log("Initializing embed-iframe");
|
||||
if (typeof window !== "undefined" && !location.search.includes("prerender=true")) {
|
||||
sdkActionManager?.on("*", (e) => {
|
||||
const detail = e.detail;
|
||||
//console.log(detail.fullType, detail.type, detail.data);
|
||||
messageParent(detail);
|
||||
});
|
||||
|
||||
// If embed link is opened in top, and not in iframe. Let the page be visible.
|
||||
if (top === window) {
|
||||
unhideBody();
|
||||
window.addEventListener("message", (e) => {
|
||||
const data: Record<string, any> = e.data;
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const method: keyof typeof methods = data.method;
|
||||
if (data.originator === "CAL" && typeof method === "string") {
|
||||
methods[method]?.(data.arg);
|
||||
}
|
||||
});
|
||||
|
||||
sdkActionManager?.on("*", (e) => {
|
||||
const detail = e.detail;
|
||||
//console.log(detail.fullType, detail.type, detail.data);
|
||||
log(detail);
|
||||
messageParent(detail);
|
||||
});
|
||||
|
||||
window.addEventListener("message", (e) => {
|
||||
const data: Record<string, any> = e.data;
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
const method: keyof typeof methods = data.method;
|
||||
if (data.originator === "CAL" && typeof method === "string") {
|
||||
methods[method]?.(data.arg);
|
||||
}
|
||||
});
|
||||
|
||||
keepParentInformedAboutDimensionChanges();
|
||||
sdkActionManager?.fire("iframeReady", {});
|
||||
}
|
||||
keepParentInformedAboutDimensionChanges();
|
||||
sdkActionManager?.fire("iframeReady", {});
|
||||
}
|
||||
|
|
|
@ -8,10 +8,7 @@ import { SdkActionManager } from "./sdk-action-manager";
|
|||
declare module "*.css";
|
||||
|
||||
type Namespace = string;
|
||||
type Config = {
|
||||
origin: string;
|
||||
debug: 1;
|
||||
};
|
||||
type Config = Record<"origin", "string">;
|
||||
|
||||
const globalCal = (window as CalWindow).Cal;
|
||||
|
||||
|
@ -138,8 +135,9 @@ export class Cal {
|
|||
queryObject?: Record<string, string | string[]>;
|
||||
}) {
|
||||
const iframe = (this.iframe = document.createElement("iframe"));
|
||||
// FIXME: scrolling seems deprecated, though it works on Chrome. What's the recommended way to do it?
|
||||
iframe.scrolling = "no";
|
||||
iframe.className = "cal-embed";
|
||||
iframe.name = "cal-embed";
|
||||
const config = this.getConfig();
|
||||
|
||||
// Prepare searchParams from config
|
||||
|
@ -154,9 +152,6 @@ export class Cal {
|
|||
|
||||
const urlInstance = new URL(`${config.origin}/${calLink}`);
|
||||
urlInstance.searchParams.set("embed", this.namespace);
|
||||
if (config.debug) {
|
||||
urlInstance.searchParams.set("debug", config.debug);
|
||||
}
|
||||
|
||||
// Merge searchParams from config onto the URL which might have query params already
|
||||
//@ts-ignore
|
||||
|
@ -167,14 +162,13 @@ export class Cal {
|
|||
return iframe;
|
||||
}
|
||||
|
||||
init(namespaceOrConfig?: string | Config, config: Config = {} as Config) {
|
||||
if (typeof namespaceOrConfig !== "string") {
|
||||
config = (namespaceOrConfig || {}) as Config;
|
||||
init(namespaceOrConfig: string | Config, config: Config = {} as Config) {
|
||||
if (namespaceOrConfig.hasOwnProperty("origin")) {
|
||||
config = namespaceOrConfig as Config;
|
||||
}
|
||||
if (config?.origin) {
|
||||
this.__config.origin = config.origin;
|
||||
}
|
||||
this.__config.debug = config.debug;
|
||||
}
|
||||
|
||||
getConfig() {
|
||||
|
@ -313,8 +307,7 @@ export class Cal {
|
|||
|
||||
constructor(namespace: string, q: InstructionQueue) {
|
||||
this.__config = {
|
||||
// Keep cal.com hardcoded till the time embed.js deployment to cal.com/embed.js is automated. This is to prevent accidentally pushing of localhost domain to production
|
||||
origin: /*import.meta.env.NEXT_PUBLIC_WEBSITE_URL || */ "https://cal.com",
|
||||
origin: import.meta.env.NEXT_PUBLIC_WEBSITE_URL || "https://cal.com",
|
||||
};
|
||||
this.namespace = namespace;
|
||||
this.actionManager = new SdkActionManager(namespace);
|
||||
|
@ -330,16 +323,12 @@ export class Cal {
|
|||
this.actionManager.on("dimension-changed", (e) => {
|
||||
const { data } = e.detail;
|
||||
const iframe = this.iframe!;
|
||||
|
||||
if (!iframe) {
|
||||
// Iframe might be pre-rendering
|
||||
return;
|
||||
}
|
||||
let proposedHeightByIframeWebsite = data.iframeHeight;
|
||||
iframe.style.height = proposedHeightByIframeWebsite + "px";
|
||||
// It ensures that if the iframe is so tall that it can't fit in the parent window without scroll. Then force the scroll by restricting the max-height to innerHeight
|
||||
// This case is reproducible when viewing in ModalBox on Mobile.
|
||||
iframe.style.maxHeight = window.innerHeight + "px";
|
||||
let proposedHeightByIframeWebsite = parseFloat(getComputedStyle(iframe).height) + data.hiddenHeight;
|
||||
iframe.style.height = proposedHeightByIframeWebsite;
|
||||
});
|
||||
|
||||
this.actionManager.on("iframeReady", (e) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"extends": "@calcom/tsconfig/base.json",
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"baseUrl": "."
|
||||
},
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
"version": "0.1.0",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"dev": "vite --port=3002",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"type-check": "tsc --pretty --noEmit",
|
||||
"lint": "eslint --ext .ts,.js src"
|
||||
},
|
||||
|
|
|
@ -22,9 +22,6 @@ export interface CalWindow extends Window {
|
|||
export default function EmbedSnippet(url = "https://cal.com/embed.js") {
|
||||
/*! Copy the code below and paste it in script tag of your website */
|
||||
(function (C: CalWindow, A, L) {
|
||||
let p = function (a: any, ar: any) {
|
||||
a.q.push(ar);
|
||||
};
|
||||
let d = C.document;
|
||||
C.Cal =
|
||||
C.Cal ||
|
||||
|
@ -40,14 +37,14 @@ export default function EmbedSnippet(url = "https://cal.com/embed.js") {
|
|||
|
||||
if (ar[0] === L) {
|
||||
const api: { (): void; q: any[] } = function () {
|
||||
p(api, arguments);
|
||||
api.q.push(arguments);
|
||||
};
|
||||
const namespace = ar[1];
|
||||
const namespace = arguments[1];
|
||||
api.q = api.q || [];
|
||||
typeof namespace === "string" ? (cal.ns![namespace] = api) && p(api, ar) : p(cal, ar);
|
||||
namespace ? (cal.ns![namespace] = api) : null;
|
||||
return;
|
||||
}
|
||||
p(cal, ar);
|
||||
cal.q!.push(ar as unknown as Instruction);
|
||||
};
|
||||
})(
|
||||
window,
|
||||
|
|
|
@ -4,7 +4,7 @@ const { defineConfig } = require("vite");
|
|||
module.exports = defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, "src", "index.ts"),
|
||||
entry: path.resolve(__dirname, "index.ts"),
|
||||
name: "snippet",
|
||||
fileName: (format) => `snippet.${format}.js`,
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue