Skip to content

Commit be40443

Browse files
authored
fix: Do not self import @calcom/ui (#20050)
* fix: Do not self import @calcom/ui * Make translations optional * Fix mocking implementation of Button (never worked) * Ensure other libraries can resolve AppListCard
1 parent b1ebefb commit be40443

28 files changed

+201
-124
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,25 @@
11
"use client";
22

3-
import { usePathname, useRouter } from "next/navigation";
4-
import type { ReactNode } from "react";
5-
import { useEffect, useRef, useState } from "react";
6-
import { z } from "zod";
3+
import dynamic from "next/dynamic";
4+
import { useMemo } from "react";
75

8-
import type { CredentialOwner } from "@calcom/app-store/types";
96
import { useIsPlatform } from "@calcom/atoms/hooks/useIsPlatform";
10-
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
11-
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
12-
import { AppListCard as AppListCardComponent } from "@calcom/ui";
13-
14-
type ShouldHighlight =
15-
| {
16-
slug: string;
17-
shouldHighlight: true;
18-
}
19-
| {
20-
shouldHighlight?: never;
21-
slug?: never;
22-
};
23-
24-
export type AppListCardProps = {
25-
logo?: string;
26-
title: string;
27-
description: string;
28-
actions?: ReactNode;
29-
isDefault?: boolean;
30-
isTemplate?: boolean;
31-
invalidCredential?: boolean;
32-
children?: ReactNode;
33-
credentialOwner?: CredentialOwner;
34-
className?: string;
35-
} & ShouldHighlight;
36-
37-
const schema = z.object({ hl: z.string().optional() });
38-
39-
function AppListCardPlatformWrapper(props: AppListCardProps) {
40-
const logo = `https://app.cal.com${props.logo}`;
41-
return <AppListCardComponent {...props} logo={logo} />;
42-
}
43-
function AppListCardWebWrapper(props: AppListCardProps) {
44-
const { slug, shouldHighlight } = props;
45-
const {
46-
data: { hl },
47-
} = useTypedQuery(schema);
48-
const router = useRouter();
49-
const [highlight, setHighlight] = useState(shouldHighlight && hl === slug);
50-
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
51-
const searchParams = useCompatSearchParams();
52-
const pathname = usePathname();
53-
54-
useEffect(() => {
55-
if (shouldHighlight && highlight && searchParams !== null && pathname !== null) {
56-
timeoutRef.current = setTimeout(() => {
57-
const _searchParams = new URLSearchParams(searchParams.toString());
58-
_searchParams.delete("hl");
59-
_searchParams.delete("category"); // this comes from params, not from search params
60-
61-
setHighlight(false);
62-
63-
const stringifiedSearchParams = _searchParams.toString();
64-
65-
router.replace(`${pathname}${stringifiedSearchParams !== "" ? `?${stringifiedSearchParams}` : ""}`);
66-
}, 3000);
67-
}
68-
return () => {
69-
if (timeoutRef.current) {
70-
clearTimeout(timeoutRef.current);
71-
timeoutRef.current = null;
72-
}
73-
};
74-
}, [highlight, pathname, router, searchParams, shouldHighlight]);
75-
76-
return <AppListCardComponent {...props} />;
77-
}
7+
import type { AppListCardProps } from "@calcom/ui/components/app-list-card/AppListCard";
788

799
export default function AppListCard(props: AppListCardProps) {
8010
const isPlatform = useIsPlatform();
81-
return isPlatform ? <AppListCardPlatformWrapper {...props} /> : <AppListCardWebWrapper {...props} />;
11+
12+
// Dynamically import the correct wrapper
13+
const AppListCardWrapper = useMemo(
14+
() =>
15+
dynamic(
16+
() => (isPlatform ? import("./AppListCardPlatformWrapper") : import("./AppListCardWebWrapper")),
17+
{
18+
ssr: false, // Prevents SSR issues if needed
19+
}
20+
),
21+
[isPlatform]
22+
);
23+
24+
return <AppListCardWrapper {...props} />;
8225
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { AppListCard } from "@calcom/ui/components/app-list-card/AppListCard";
2+
import type { AppListCardProps } from "@calcom/ui/components/app-list-card/AppListCard";
3+
4+
export default function AppListCardPlatformWrapper(props: AppListCardProps) {
5+
const logo = `https://app.cal.com${props.logo}`;
6+
return <AppListCard {...props} logo={logo} />;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"use client";
2+
3+
import { usePathname, useRouter } from "next/navigation";
4+
import { useEffect, useRef, useState } from "react";
5+
import { z } from "zod";
6+
7+
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
8+
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
9+
import { AppListCard } from "@calcom/ui/components/app-list-card/AppListCard";
10+
import type { AppListCardProps } from "@calcom/ui/components/app-list-card/AppListCard";
11+
12+
const schema = z.object({ hl: z.string().optional() });
13+
14+
export default function AppListCardWebWrapper(props: AppListCardProps) {
15+
const { slug, shouldHighlight } = props;
16+
const {
17+
data: { hl },
18+
} = useTypedQuery(schema);
19+
const router = useRouter();
20+
const [highlight, setHighlight] = useState(shouldHighlight && hl === slug);
21+
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
22+
const searchParams = useCompatSearchParams();
23+
const pathname = usePathname();
24+
25+
useEffect(() => {
26+
if (shouldHighlight && highlight && searchParams !== null && pathname !== null) {
27+
timeoutRef.current = setTimeout(() => {
28+
const _searchParams = new URLSearchParams(searchParams.toString());
29+
_searchParams.delete("hl");
30+
_searchParams.delete("category"); // this comes from params, not from search params
31+
32+
setHighlight(false);
33+
34+
const stringifiedSearchParams = _searchParams.toString();
35+
36+
router.replace(`${pathname}${stringifiedSearchParams !== "" ? `?${stringifiedSearchParams}` : ""}`);
37+
}, 3000);
38+
}
39+
return () => {
40+
if (timeoutRef.current) {
41+
clearTimeout(timeoutRef.current);
42+
timeoutRef.current = null;
43+
}
44+
};
45+
}, [highlight, pathname, router, searchParams, shouldHighlight]);
46+
47+
return <AppListCard {...props} />;
48+
}

packages/types/CredentialOwner.d.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export type CredentialOwner = {
2+
name: string | null;
3+
avatar?: string | null;
4+
teamId?: number;
5+
credentialId?: number;
6+
readOnly?: boolean;
7+
};

packages/ui/components/TokenHandler/TokenHandler.tsx

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
import { useLocale } from "@calcom/lib/hooks/useLocale";
2-
import { Label, Input } from "@calcom/ui";
1+
// TODO: This file is currently unused.
2+
import { Label } from "../form/inputs/Label";
3+
import { Input } from "../form/inputs/TextField";
34

45
type Digit = {
56
value: number;
67
onChange: () => void;
78
};
9+
10+
type Translations = {
11+
labelText?: string;
12+
};
13+
814
type PropType = {
915
digits: Digit[];
1016
digitClassName: string;
17+
translations?: Translations;
1118
};
1219

13-
const TokenHandler = ({ digits, digitClassName }: PropType) => {
14-
const { t } = useLocale();
20+
const TokenHandler = ({ digits, digitClassName, translations }: PropType) => {
21+
const { labelText = "Code" } = translations ?? {};
1522

1623
return (
1724
<div>
18-
<Label htmlFor="code">{t("code")}</Label>
25+
<Label htmlFor="code">{labelText}</Label>
1926
<div className="flex flex-row justify-between">
2027
{digits.map((element, index) => (
2128
<Input

packages/ui/components/app-list-card/AppListCard.tsx

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,40 @@
11
"use client";
22

3-
import type { AppListCardProps } from "@calcom/features/apps/components/AppListCard";
3+
import type { ReactNode } from "react";
4+
45
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
56
import { useLocale } from "@calcom/lib/hooks/useLocale";
6-
import { Avatar, Badge, Icon, ListItemText } from "@calcom/ui";
7+
import type { CredentialOwner } from "@calcom/types/CredentialOwner";
78
import classNames from "@calcom/ui/classNames";
89

10+
import { Avatar } from "../avatar/Avatar";
11+
import { Badge } from "../badge/Badge";
12+
import { Icon } from "../icon/Icon";
13+
import { ListItemText } from "../list/List";
14+
15+
type ShouldHighlight =
16+
| {
17+
slug: string;
18+
shouldHighlight: true;
19+
}
20+
| {
21+
shouldHighlight?: never;
22+
slug?: never;
23+
};
24+
25+
export type AppListCardProps = {
26+
logo?: string;
27+
title: string;
28+
description: string;
29+
actions?: ReactNode;
30+
isDefault?: boolean;
31+
isTemplate?: boolean;
32+
invalidCredential?: boolean;
33+
children?: ReactNode;
34+
credentialOwner?: CredentialOwner;
35+
className?: string;
36+
} & ShouldHighlight;
37+
938
export const AppListCard = (props: AppListCardProps & { highlight?: boolean }) => {
1039
const { t } = useLocale();
1140
const {

packages/ui/components/apps/AppCard.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
1313
import type { UserAdminTeams } from "@calcom/lib/server/repository/user";
1414
import type { AppFrontendPayload as App } from "@calcom/types/App";
1515
import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential";
16-
import type { ButtonProps } from "@calcom/ui";
17-
import { Badge, showToast } from "@calcom/ui";
1816
import classNames from "@calcom/ui/classNames";
1917

18+
import { Badge } from "../badge/Badge";
19+
import type { ButtonProps } from "../button";
2020
import { Button } from "../button";
21+
import { showToast } from "../toast/showToast";
2122

2223
interface AppCardProps {
2324
app: App;

packages/ui/components/avatar/UserAvatar.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
22
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
33
import type { User } from "@calcom/prisma/client";
44
import type { UserProfile } from "@calcom/types/UserProfile";
5-
import { Avatar } from "@calcom/ui";
65
import classNames from "@calcom/ui/classNames";
76

7+
import { Avatar } from "./Avatar";
8+
89
type Organization = {
910
id: number;
1011
slug: string | null;

packages/ui/components/avatar/UserAvatarGroup.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
22
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
33
import type { User } from "@calcom/prisma/client";
44
import type { UserProfile } from "@calcom/types/UserProfile";
5-
import { AvatarGroup } from "@calcom/ui";
5+
6+
import { AvatarGroup } from "./AvatarGroup";
67

78
type UserAvatarProps = Omit<React.ComponentProps<typeof AvatarGroup>, "items"> & {
89
users: (Pick<User, "name" | "username" | "avatarUrl"> & {

packages/ui/components/avatar/UserAvatarGroupWithOrg.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
33
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
44
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
55
import type { Team, User } from "@calcom/prisma/client";
6-
import { AvatarGroup } from "@calcom/ui";
6+
7+
import { AvatarGroup } from "./AvatarGroup";
78

89
type UserAvatarProps = Omit<React.ComponentProps<typeof AvatarGroup>, "items"> & {
910
users: (Pick<User, "name" | "username" | "avatarUrl"> & {

packages/ui/components/button/Button.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import React, { forwardRef } from "react";
66

77
import classNames from "@calcom/ui/classNames";
88

9-
import { Icon } from "../icon";
10-
import type { IconName } from "../icon";
11-
import { Tooltip } from "../tooltip";
9+
import { Icon } from "../icon/Icon";
10+
import type { IconName } from "../icon/Icon";
11+
import { Tooltip } from "../tooltip/Tooltip";
1212

1313
type InferredVariantProps = VariantProps<typeof buttonClasses>;
1414

packages/ui/components/disconnect-calendar-integration/DisconnectIntegration.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { useLocale } from "@calcom/lib/hooks/useLocale";
2-
import type { ButtonProps } from "@calcom/ui";
3-
import { Button, ConfirmationDialogContent, Dialog, DialogTrigger } from "@calcom/ui";
2+
3+
import type { ButtonProps } from "../button/Button";
4+
import { Button } from "../button/Button";
5+
import { ConfirmationDialogContent } from "../dialog/ConfirmationDialogContent";
6+
import { Dialog, DialogTrigger } from "../dialog/Dialog";
47

58
export const DisconnectIntegrationComponent = ({
69
label,

packages/ui/components/filter-select/index.tsx

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import type { IconName } from "@calcom/ui";
1+
import classNames from "@calcom/ui/classNames";
2+
3+
import { Badge } from "../badge/Badge";
4+
import { Button } from "../button/Button";
25
import {
3-
Button,
4-
Popover,
5-
PopoverTrigger,
6-
PopoverContent,
76
Command,
87
CommandInput,
98
CommandList,
109
CommandEmpty,
1110
CommandGroup,
1211
CommandItem,
1312
CommandSeparator,
14-
Badge,
15-
Icon,
16-
} from "@calcom/ui";
17-
import classNames from "@calcom/ui/classNames";
13+
} from "../command/Command";
14+
import type { IconName } from "../icon/Icon";
15+
import { Icon } from "../icon/Icon";
16+
import { Popover, PopoverTrigger, PopoverContent } from "../popover/Popover";
1817

1918
export interface FilterOption {
2019
value: string | number;

packages/ui/components/form/color-picker/colorpicker.test.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ import { act } from "react-dom/test-utils";
44
import { vi } from "vitest";
55

66
import type { ButtonProps } from "../../button";
7-
import { Button } from "../../button";
87
import ColorPicker from "./colorpicker";
98

10-
vi.mock("@calcom/ui", async () => {
9+
vi.mock("../../button/Button", async () => {
10+
const ButtonMock = (await import("../../button/Button")).Button;
11+
return {
12+
Button: ({ tooltip, ...rest }: ButtonProps) => <ButtonMock {...rest}>{tooltip}</ButtonMock>,
13+
};
14+
});
15+
16+
vi.mock("../icon/Icon", async () => {
1117
return {
1218
Icon: () => <svg data-testid="dummy-icon" />,
13-
Button: ({ tooltip, ...rest }: ButtonProps) => <Button {...rest}>{tooltip}</Button>,
1419
};
1520
});
1621

packages/ui/components/form/color-picker/colorpicker.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { useState } from "react";
55
import { HexColorInput, HexColorPicker } from "react-colorful";
66

77
import { fallBackHex, isValidHexCode } from "@calcom/lib/getBrandColours";
8-
import { Button } from "@calcom/ui";
98
import cx from "@calcom/ui/classNames";
109

10+
import { Button } from "../../button/Button";
11+
1112
export type ColorPickerProps = {
1213
defaultValue: string;
1314
onChange: (text: string) => void;
@@ -73,6 +74,7 @@ const ColorPicker = (props: ColorPickerProps) => {
7374
target="_blank"
7475
variant="icon"
7576
rel="noreferrer"
77+
aria-label="Reset to default"
7678
StartIcon="rotate-ccw"
7779
tooltip="Reset to default"
7880
onClick={() => {

packages/ui/components/form/datepicker/DatePicker.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import * as Popover from "@radix-ui/react-popover";
22
import { format } from "date-fns";
33

4-
import { Button } from "@calcom/ui";
54
import classNames from "@calcom/ui/classNames";
65

6+
import { Button } from "../../button/Button";
77
import { Calendar } from "../date-range-picker/Calendar";
88

99
type Props = {

0 commit comments

Comments
 (0)