fix: brand colors set by user not respected in embed (#17399)

Co-authored-by: Hariom <hariombalhara@gmail.com>
This commit is contained in:
Anwar Sadath
2024-12-03 04:10:58 +00:00
committed by GitHub
co-authored by Hariom
parent 6dcb6aece4
commit 3a472997ff
6 changed files with 117 additions and 38 deletions
+37 -19
View File
@@ -17,7 +17,7 @@ import { useTimePreferences } from "@calcom/features/bookings/lib/timePreference
import DatePicker from "@calcom/features/calendars/DatePicker";
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
import { useSlotsForDate } from "@calcom/features/schedules/lib/use-schedule/useSlotsForDate";
import { APP_NAME } from "@calcom/lib/constants";
import { APP_NAME, DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants";
import { weekdayToWeekIndex } from "@calcom/lib/date-fns";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -41,10 +41,17 @@ import {
TimezoneSelect,
} from "@calcom/ui";
import { buildCssVarsPerTheme } from "./lib/buildCssVarsPerTheme";
import { getDimension } from "./lib/getDimension";
import type { EmbedTabs, EmbedType, EmbedTypes, PreviewState } from "./types";
type EventType = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"] | undefined;
type EmbedDialogProps = {
types: EmbedTypes;
tabs: EmbedTabs;
eventTypeHideOptionDisabled: boolean;
defaultBrandColor: { brandColor: string | null; darkBrandColor: string | null } | null;
};
const enum Theme {
auto = "auto",
@@ -525,13 +532,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
namespace,
eventTypeHideOptionDisabled,
types,
}: {
defaultBrandColor,
}: EmbedDialogProps & {
embedType: EmbedType;
embedUrl: string;
tabs: EmbedTabs;
namespace: string;
eventTypeHideOptionDisabled: boolean;
types: EmbedTypes;
}) => {
const { t } = useLocale();
const searchParams = useCompatSearchParams();
@@ -578,6 +584,19 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
const defaultConfig = {
layout: BookerLayouts.MONTH_VIEW,
};
const paletteDefaultValue = (paletteName: string) => {
if (paletteName === "brandColor") {
return defaultBrandColor?.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR;
}
if (paletteName === "darkBrandColor") {
return defaultBrandColor?.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR;
}
return "#000000";
};
const [previewState, setPreviewState] = useState<PreviewState>({
inline: {
width: "100%",
@@ -594,7 +613,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
} as PreviewState["elementClick"],
hideEventTypeDetails: false,
palette: {
brandColor: "#000000",
brandColor: defaultBrandColor?.brandColor ?? null,
darkBrandColor: defaultBrandColor?.darkBrandColor ?? null,
},
});
@@ -614,7 +634,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
return null;
}
const addToPalette = (update: (typeof previewState)["palette"]) => {
const addToPalette = (update: Partial<(typeof previewState)["palette"]>) => {
setPreviewState((previewState) => {
return {
...previewState,
@@ -657,11 +677,10 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
theme: previewState.theme,
layout: previewState.layout,
hideEventTypeDetails: previewState.hideEventTypeDetails,
styles: {
branding: {
...previewState.palette,
},
},
cssVarsPerTheme: buildCssVarsPerTheme({
brandColor: previewState.palette.brandColor,
darkBrandColor: previewState.palette.darkBrandColor,
}),
},
});
@@ -994,7 +1013,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
</div>
) : null}
{[
{ name: "brandColor", title: "Brand Color" },
{ name: "brandColor", title: "light_brand_color" },
{ name: "darkBrandColor", title: "dark_brand_color" },
// { name: "lightColor", title: "Light Color" },
// { name: "lighterColor", title: "Lighter Color" },
// { name: "lightestColor", title: "Lightest Color" },
@@ -1002,12 +1022,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
// { name: "medianColor", title: "Median Color" },
].map((palette) => (
<Label key={palette.name} className="mb-6">
<div className="mb-2">{palette.title}</div>
<div className="mb-2">{t(palette.title)}</div>
<div className="w-full">
<ColorPicker
popoverAlign="start"
container={dialogContentRef?.current ?? undefined}
defaultValue="#000000"
defaultValue={paletteDefaultValue(palette.name)}
onChange={(color) => {
addToPalette({
[palette.name as keyof (typeof previewState)["palette"]]: color,
@@ -1164,11 +1184,8 @@ export const EmbedDialog = ({
types,
tabs,
eventTypeHideOptionDisabled,
}: {
types: EmbedTypes;
tabs: EmbedTabs;
eventTypeHideOptionDisabled: boolean;
}) => {
defaultBrandColor,
}: EmbedDialogProps) => {
const searchParams = useCompatSearchParams();
const embedUrl = (searchParams?.get("embedUrl") || "") as string;
const namespace = (searchParams?.get("namespace") || "") as string;
@@ -1184,6 +1201,7 @@ export const EmbedDialog = ({
tabs={tabs}
types={types}
eventTypeHideOptionDisabled={eventTypeHideOptionDisabled}
defaultBrandColor={defaultBrandColor}
/>
)}
</Dialog>
+11 -1
View File
@@ -1,13 +1,23 @@
import type { ComponentProps } from "react";
import { trpc } from "@calcom/trpc/react";
import { EmbedButton, EmbedDialog } from "./Embed";
import { tabs } from "./lib/EmbedTabs";
import { useEmbedTypes } from "./lib/hooks";
export const EventTypeEmbedDialog = () => {
const types = useEmbedTypes();
const { data: user } = trpc.viewer.me.useQuery();
return <EmbedDialog types={types} tabs={tabs} eventTypeHideOptionDisabled={false} />;
return (
<EmbedDialog
types={types}
tabs={tabs}
eventTypeHideOptionDisabled={false}
defaultBrandColor={user ? { brandColor: user.brandColor, darkBrandColor: user.darkBrandColor } : null}
/>
);
};
export const EventTypeEmbedButton = (props: ComponentProps<typeof EmbedButton>) => {
+10 -1
View File
@@ -1,14 +1,23 @@
import type { ComponentProps } from "react";
import { EmbedDialog, EmbedButton } from "@calcom/features/embed/Embed";
import { trpc } from "@calcom/trpc/react";
import { tabs } from "./lib/EmbedTabs";
import { useEmbedTypes } from "./lib/hooks";
export const RoutingFormEmbedDialog = () => {
const types = useEmbedTypes();
const { data: user } = trpc.viewer.me.useQuery();
const routingFormTypes = types.filter((type) => type.type !== "email");
return <EmbedDialog types={routingFormTypes} tabs={tabs} eventTypeHideOptionDisabled={true} />;
return (
<EmbedDialog
types={routingFormTypes}
tabs={tabs}
eventTypeHideOptionDisabled={true}
defaultBrandColor={user ? { brandColor: user.brandColor, darkBrandColor: user.darkBrandColor } : null}
/>
);
};
export const RoutingFormEmbedButton = (props: ComponentProps<typeof EmbedButton>) => {
+18 -16
View File
@@ -9,6 +9,7 @@ import { TextArea } from "@calcom/ui";
import type { EmbedFramework, EmbedType, PreviewState } from "../types";
import { Codes } from "./EmbedCodes";
import { buildCssVarsPerTheme } from "./buildCssVarsPerTheme";
import { embedLibUrl, EMBED_PREVIEW_HTML_URL } from "./constants";
import { getApiNameForReactSnippet, getApiNameForVanillaJsSnippet } from "./getApiName";
import { getDimension } from "./getDimension";
@@ -175,25 +176,27 @@ const getEmbedTypeSpecificString = ({
let uiInstructionStringArg: {
apiName: string;
theme: PreviewState["theme"];
brandColor: string;
brandColor: string | null;
darkBrandColor: string | null;
hideEventTypeDetails: boolean;
layout?: BookerLayout;
};
const baseUiInstructionStringArg = {
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
darkBrandColor: previewState.palette.darkBrandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
if (embedFramework === "react") {
uiInstructionStringArg = {
...baseUiInstructionStringArg,
apiName: getApiNameForReactSnippet({ mainApiName: "cal" }),
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
} else {
uiInstructionStringArg = {
...baseUiInstructionStringArg,
apiName: getApiNameForVanillaJsSnippet({ namespace, mainApiName: "Cal" }),
theme: previewState.theme,
brandColor: previewState.palette.brandColor,
hideEventTypeDetails: previewState.hideEventTypeDetails,
layout: previewState.layout,
};
}
if (!frameworkCodes[embedType]) {
@@ -230,27 +233,26 @@ const getEmbedUIInstructionString = ({
apiName,
theme,
brandColor,
darkBrandColor,
hideEventTypeDetails,
layout,
}: {
apiName: string;
theme?: string;
brandColor: string;
brandColor: string | null;
darkBrandColor: string | null;
hideEventTypeDetails: boolean;
layout?: string;
}) => {
theme = theme !== "auto" ? theme : undefined;
return getInstructionString({
apiName,
instructionName: "ui",
instructionArg: {
theme,
styles: {
branding: {
brandColor,
},
},
hideEventTypeDetails: hideEventTypeDetails,
cssVarsPerTheme: buildCssVarsPerTheme({ brandColor, darkBrandColor }),
hideEventTypeDetails,
layout,
},
});
@@ -0,0 +1,39 @@
function _buildCssVarsPerTheme({
light,
dark,
}: {
light: { "cal-brand": string | null };
dark: { "cal-brand": string | null };
}) {
// Setting this value should remove it from the API
const VALUE_WHEN_WE_DONT_WANT_IT_IN_API = undefined;
const cssVarsPerTheme = Object.entries({ light, dark }).reduce((acc, [theme, themeCssVars]) => {
if (!Object.values(themeCssVars).some(Boolean)) return acc;
const truthyValues = Object.fromEntries(
Object.entries(themeCssVars).filter(([_, value]) => Boolean(value))
);
return {
...acc,
[theme]: truthyValues,
};
}, {});
if (Object.keys(cssVarsPerTheme).length === 0) return VALUE_WHEN_WE_DONT_WANT_IT_IN_API;
return cssVarsPerTheme;
}
export function buildCssVarsPerTheme({
brandColor,
darkBrandColor,
}: {
brandColor: string | null;
darkBrandColor: string | null;
}) {
return _buildCssVarsPerTheme({
light: { "cal-brand": brandColor },
dark: { "cal-brand": darkBrandColor },
});
}
+2 -1
View File
@@ -36,7 +36,8 @@ export type PreviewState = {
"element-click"
>;
palette: {
brandColor: string;
brandColor: string | null;
darkBrandColor: string | null;
};
hideEventTypeDetails: boolean;
layout: BookerLayouts;