Files
calendar/packages/features/feature-opt-in/components/FeatureOptInBannerWrapper.tsx
T
Eunjae LeeGitHubunknown <>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1e31ac089a fix: use createPortal for FeatureOptInBanner to avoid Intercom widget conflict (#27121)
* fix: use createPortal for FeatureOptInBanner to avoid Intercom widget conflict

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: guard portal rendering against non-browser environments

Add typeof document check before accessing document.body in createPortal
to prevent SSR/test crashes when document is undefined.

Co-Authored-By: unknown <>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-01-26 14:07:29 +00:00

70 lines
1.9 KiB
TypeScript

"use client";
import type { OptInFeatureConfig } from "@calcom/features/feature-opt-in/config";
import type { ReactElement } from "react";
import { createPortal } from "react-dom";
import { FeatureOptInBanner } from "./FeatureOptInBanner";
import type { FeatureOptInMutations } from "./FeatureOptInConfirmDialog";
import { FeatureOptInConfirmDialog } from "./FeatureOptInConfirmDialog";
type UserRoleContext = {
isOrgAdmin: boolean;
orgId: number | null;
adminTeamIds: number[];
adminTeamNames: { id: number; name: string }[];
};
type FeatureOptInBannerState = {
shouldShow: boolean;
isLoading: boolean;
featureConfig: OptInFeatureConfig | null;
canOptIn: boolean;
blockingReason: string | null;
userRoleContext: UserRoleContext | null;
isDialogOpen: boolean;
openDialog: () => void;
closeDialog: () => void;
dismiss: () => void;
markOptedIn: () => void;
mutations: FeatureOptInMutations;
};
interface FeatureOptInBannerWrapperProps {
state: FeatureOptInBannerState;
}
function FeatureOptInBannerWrapper({ state }: FeatureOptInBannerWrapperProps): ReactElement | null {
if (!state.featureConfig) {
return null;
}
return (
<>
{state.shouldShow &&
typeof document !== "undefined" &&
createPortal(
<FeatureOptInBanner
featureConfig={state.featureConfig}
onDismiss={state.dismiss}
onOpenDialog={state.openDialog}
/>,
document.body
)}
{state.userRoleContext && (
<FeatureOptInConfirmDialog
isOpen={state.isDialogOpen}
onClose={state.closeDialog}
onDismissBanner={state.dismiss}
onOptInSuccess={state.markOptedIn}
featureConfig={state.featureConfig}
userRoleContext={state.userRoleContext}
mutations={state.mutations}
/>
)}
</>
);
}
export default FeatureOptInBannerWrapper;