fix: improve booking page error boundary + remove dynamic imports from Booker (#20560)

* improve booking page error boundary + remove dynamic imports from Booker

* safer

* fix

* fix test

* change name
This commit is contained in:
Benny Joo authored and GitHub committed 2025-04-07 09:51:14 +03:00
1 parent d158a1e007
commit 5c7d7579ee
5 files changed
+28 -18

No files matched your search

+2 -1
View File
@@ -17,7 +17,7 @@ export type ErrorProps = {
reset?: () => void;
};
export default function Error({ error }: ErrorProps) {
export default function Error({ error, reset }: ErrorProps) {
React.useEffect(() => {
log.error(error);
@@ -51,6 +51,7 @@ export default function Error({ error }: ErrorProps) {
return (
<ErrorPage
reset={reset}
statusCode={processedError.statusCode}
error={processedError}
message={processedError.message}
@@ -7,8 +7,11 @@ import { ErrorPage } from "./error-page";
export default function BookingPageErrorBoundary({ children }: { children: React.ReactNode }) {
return (
<ErrorBoundary
fallbackRender={({ error }) => <ErrorPage error={error} message={`${error}`} displayDebug={true} />}
fallbackRender={({ error, resetErrorBoundary }) => (
<ErrorPage reset={resetErrorBoundary} error={error} message={`${error}`} displayDebug={true} />
)}
onError={(error, info) => {
console.error(error);
captureReactException(error, info);
}}>
{children}
+7 -3
View File
@@ -12,6 +12,7 @@ type Props = {
/** Display debugging information */
displayDebug?: boolean;
children?: never;
reset?: () => void;
};
const defaultProps = {
@@ -53,7 +54,10 @@ const ErrorDebugPanel: React.FC<{ error: Props["error"]; children?: never }> = (
export const ErrorPage: React.FC<Props> = (props) => {
const { message, statusCode, error, displayDebug } = { ...defaultProps, ...props };
const handleReset = () => {
window.location.reload();
props.reset?.();
};
return (
<>
<div className="bg-subtle flex h-screen">
@@ -77,8 +81,8 @@ export const ErrorPage: React.FC<Props> = (props) => {
</div>
<Button href="mailto:support@cal.com">Contact Support</Button>
<Button color="secondary" href="javascript:history.back()" className="ml-2">
Go back
<Button color="secondary" className="ml-2" onClick={handleReset}>
Try again
</Button>
</div>
</div>
+6 -13
View File
@@ -1,5 +1,4 @@
import { AnimatePresence, LazyMotion, m } from "framer-motion";
import dynamic from "next/dynamic";
import { useEffect, useMemo, useRef } from "react";
import StickyBox from "react-sticky-box";
import { Toaster } from "sonner";
@@ -8,6 +7,8 @@ import { shallow } from "zustand/shallow";
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
import { useIsPlatformBookerEmbed } from "@calcom/atoms/hooks/useIsPlatformBookerEmbed";
import dayjs from "@calcom/dayjs";
import PoweredBy from "@calcom/ee/components/PoweredBy";
import TurnstileCaptcha from "@calcom/features/auth/Turnstile";
import useSkipConfirmStep from "@calcom/features/bookings/Booker/components/hooks/useSkipConfirmStep";
import { getQueryParam } from "@calcom/features/bookings/Booker/utils/query-param";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
@@ -16,11 +17,13 @@ import { CLOUDFLARE_SITE_ID, CLOUDFLARE_USE_TURNSTILE_IN_BOOKER } from "@calcom/
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { BookerLayouts } from "@calcom/prisma/zod-utils";
import classNames from "@calcom/ui/classNames";
import { UnpublishedEntity } from "@calcom/ui/components/unpublished-entity";
import { VerifyCodeDialog } from "../components/VerifyCodeDialog";
import { AvailableTimeSlots } from "./components/AvailableTimeSlots";
import { BookEventForm } from "./components/BookEventForm";
import { BookFormAsModal } from "./components/BookEventForm/BookFormAsModal";
import { DatePicker } from "./components/DatePicker";
import { DryRunMessage } from "./components/DryRunMessage";
import { EventMeta } from "./components/EventMeta";
import { HavingTroubleFindingTime } from "./components/HavingTroubleFindingTime";
@@ -33,22 +36,12 @@ import { BookerSection } from "./components/Section";
import { NotFound } from "./components/Unavailable";
import { useIsQuickAvailabilityCheckFeatureEnabled } from "./components/hooks/useIsQuickAvailabilityCheckFeatureEnabled";
import { fadeInLeft, getBookerSizeClassNames, useBookerResizeAnimation } from "./config";
import framerFeatures from "./framer-features";
import { useBookerStore } from "./store";
import type { BookerProps, WrappedBookerProps } from "./types";
import { isBookingDryRun } from "./utils/isBookingDryRun";
import { isTimeSlotAvailable } from "./utils/isTimeslotAvailable";
const TurnstileCaptcha = dynamic(() => import("@calcom/features/auth/Turnstile"), { ssr: false });
const loadFramerFeatures = () => import("./framer-features").then((res) => res.default);
const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy").then((mod) => mod.default));
const UnpublishedEntity = dynamic(() =>
import("@calcom/ui/components/unpublished-entity").then((mod) => mod.UnpublishedEntity)
);
const DatePicker = dynamic(() => import("./components/DatePicker").then((mod) => mod.DatePicker), {
ssr: false,
});
const BookerComponent = ({
username,
eventSlug,
@@ -586,7 +579,7 @@ const BookerComponent = ({
export const Booker = (props: BookerProps & WrappedBookerProps) => {
return (
<LazyMotion strict features={loadFramerFeatures}>
<LazyMotion strict features={framerFeatures}>
<BookerComponent {...props} />
</LazyMotion>
);
@@ -20,6 +20,13 @@ import { Booker } from "../Booker";
import { useBookerStore } from "../store";
import type { BookerState } from "../types";
vi.mock("framer-motion", async (importOriginal) => {
const actual = await importOriginal();
return {
...actual,
};
});
// Mock components that we don't want to test
vi.mock("../components/BookEventForm", () => ({
BookEventForm: ({
@@ -176,6 +183,8 @@ describe("Booker", () => {
beforeEach(() => {
constantsScenarios.set({
PUBLIC_QUICK_AVAILABILITY_ROLLOUT: 100,
POWERED_BY_URL: "https://go.cal.com/booking",
APP_NAME: "Cal.com",
});
vi.clearAllMocks();
});