2
0

first commit

This commit is contained in:
2024-08-09 00:39:27 +02:00
commit 79688abe2e
5698 changed files with 497838 additions and 0 deletions

View File

@@ -0,0 +1,135 @@
import { usePathname, useRouter } from "next/navigation";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { ButtonColor } from "@calcom/ui";
import {
Avatar,
Button,
Dropdown,
DropdownItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@calcom/ui";
export interface Option {
platform?: boolean;
teamId: number | null | undefined; // if undefined, then it's a profile
label: string | null;
image: string | null;
slug: string | null;
}
export type CreateBtnProps = {
options: Option[];
createDialog?: () => JSX.Element;
createFunction?: (teamId?: number, platform?: boolean) => void;
subtitle?: string;
buttonText?: string;
isPending?: boolean;
disableMobileButton?: boolean;
"data-testid"?: string;
color?: ButtonColor;
};
/**
* @deprecated use CreateButtonWithTeamsList instead
*/
export function CreateButton(props: CreateBtnProps) {
const { t } = useLocale();
const router = useRouter();
const searchParams = useCompatSearchParams();
const pathname = usePathname();
const {
createDialog,
options,
isPending,
createFunction,
buttonText,
disableMobileButton,
subtitle,
...restProps
} = props;
const CreateDialog = createDialog ? createDialog() : null;
const hasTeams = !!options.find((option) => option.teamId);
const platform = !!options.find((option) => option.platform);
// inject selection data into url for correct router history
const openModal = (option: Option) => {
const _searchParams = new URLSearchParams(searchParams ?? undefined);
function setParamsIfDefined(key: string, value: string | number | boolean | null | undefined) {
if (value !== undefined && value !== null) _searchParams.set(key, value.toString());
}
setParamsIfDefined("dialog", "new");
setParamsIfDefined("eventPage", option.slug);
setParamsIfDefined("teamId", option.teamId);
if (!option.teamId) {
_searchParams.delete("teamId");
}
router.push(`${pathname}?${_searchParams.toString()}`);
};
return (
<>
{!hasTeams && !platform ? (
<Button
onClick={() =>
!!CreateDialog
? openModal(options[0])
: createFunction
? createFunction(options[0].teamId || undefined)
: null
}
data-testid="create-button"
StartIcon="plus"
loading={isPending}
variant={disableMobileButton ? "button" : "fab"}
{...restProps}>
{buttonText ? buttonText : t("new")}
</Button>
) : (
<Dropdown>
<DropdownMenuTrigger asChild>
<Button
variant={disableMobileButton ? "button" : "fab"}
StartIcon="plus"
data-testid="create-button-dropdown"
loading={isPending}
{...restProps}>
{buttonText ? buttonText : t("new")}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent sideOffset={14} align="end">
<DropdownMenuLabel>
<div className="w-48 text-left text-xs">{subtitle}</div>
</DropdownMenuLabel>
{options.map((option, idx) => (
<DropdownMenuItem key={option.label}>
<DropdownItem
type="button"
data-testid={`option${option.teamId ? "-team" : ""}-${idx}`}
CustomStartIcon={<Avatar alt={option.label || ""} imageSrc={option.image} size="sm" />}
onClick={() =>
!!CreateDialog
? openModal(option)
: createFunction
? createFunction(option.teamId || undefined, option.platform)
: null
}>
{" "}
{/*improve this code */}
<span>{option.label}</span>
</DropdownItem>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</Dropdown>
)}
{searchParams?.get("dialog") === "new" && CreateDialog}
</>
);
}

View File

@@ -0,0 +1,43 @@
import { trpc } from "@calcom/trpc/react";
import type { CreateBtnProps, Option } from "./CreateButton";
import { CreateButton } from "./CreateButton";
export function CreateButtonWithTeamsList(
props: Omit<CreateBtnProps, "options"> & {
onlyShowWithTeams?: boolean;
onlyShowWithNoTeams?: boolean;
isAdmin?: boolean;
includeOrg?: boolean;
}
) {
const query = trpc.viewer.teamsAndUserProfilesQuery.useQuery({ includeOrg: props.includeOrg });
if (!query.data) return null;
const teamsAndUserProfiles: Option[] = query.data
.filter((profile) => !profile.readOnly)
.map((profile) => {
return {
teamId: profile.teamId,
label: profile.name || profile.slug,
image: profile.image,
slug: profile.slug,
};
});
if (props.isAdmin) {
teamsAndUserProfiles.push({
platform: true,
label: "Platform",
image: null,
slug: null,
teamId: null,
});
}
if (props.onlyShowWithTeams && teamsAndUserProfiles.length < 2) return null;
if (props.onlyShowWithNoTeams && teamsAndUserProfiles.length > 1) return null;
return <CreateButton {...props} options={teamsAndUserProfiles} />;
}

View File

@@ -0,0 +1,122 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { render, screen, fireEvent } from "@testing-library/react";
import { vi } from "vitest";
import type { CreateBtnProps } from "./CreateButton";
import { CreateButtonWithTeamsList } from "./CreateButtonWithTeamsList";
const runtimeMock = async (data: Array<any>) => {
const updatedTrpc = {
viewer: {
teamsAndUserProfilesQuery: {
useQuery() {
return {
data: data,
};
},
},
},
};
const mockedLib = (await import("@calcom/trpc/react")) as any;
mockedLib.trpc = updatedTrpc;
};
const renderCreateButton = (
props: Omit<CreateBtnProps, "options"> & { onlyShowWithTeams?: boolean; onlyShowWithNoTeams?: boolean }
) => {
return render(<CreateButtonWithTeamsList {...props} />);
};
describe("Create Button Tests", () => {
describe("Create Button Tests With Valid Team", () => {
beforeAll(async () => {
await runtimeMock([
{
teamId: 1,
name: "test",
slug: "create-button-test",
image: "image",
},
]);
});
test("Should render the create-button-dropdown button", () => {
const createFunction = vi.fn();
renderCreateButton({ createFunction });
const createButton = screen.getByTestId("create-button-dropdown");
expect(createButton).toBeInTheDocument();
});
});
describe("Create Button Tests With One Null Team", () => {
beforeAll(async () => {
await runtimeMock([
{
teamId: null,
name: "test",
slug: "create-button-test",
image: "image",
readOnly: false,
},
]);
});
test("Should render only the create-button button", () => {
const createFunction = vi.fn();
renderCreateButton({ createFunction });
const createButton = screen.getByTestId("create-button");
expect(screen.queryByTestId("create-button-dropdown")).not.toBeInTheDocument();
expect(createButton).toBeInTheDocument();
fireEvent.click(createButton);
expect(createFunction).toBeCalled();
});
test("Should render nothing when teamsAndUserProfiles is less than 2 and onlyShowWithTeams is true", () => {
renderCreateButton({ onlyShowWithTeams: true });
expect(screen.queryByTestId("create-button")).not.toBeInTheDocument();
expect(screen.queryByTestId("create-button-dropdown")).not.toBeInTheDocument();
});
describe("Create Button Tests With Multiple Null Teams", () => {
beforeAll(async () => {
await runtimeMock([
{
teamId: null,
name: "test",
slug: "create-button-test",
image: "image",
readOnly: false,
},
{
teamId: null,
name: "test2",
slug: "create-button-test2",
image: "image2",
readOnly: false,
},
]);
});
test("Should render only the create-button button", () => {
const createFunction = vi.fn();
renderCreateButton({ createFunction });
const createButton = screen.getByTestId("create-button");
expect(screen.queryByTestId("create-button-dropdown")).not.toBeInTheDocument();
expect(createButton).toBeInTheDocument();
fireEvent.click(createButton);
expect(createFunction).toBeCalled();
});
test("Should render nothing when teamsAndUserProfiles is greater than 1 and onlyShowWithNoTeams is true", () => {
renderCreateButton({ onlyShowWithNoTeams: true });
expect(screen.queryByTestId("create-button")).not.toBeInTheDocument();
expect(screen.queryByTestId("create-button-dropdown")).not.toBeInTheDocument();
});
});
});
});

View File

@@ -0,0 +1,2 @@
export { CreateButton } from "./CreateButton";
export { CreateButtonWithTeamsList } from "./CreateButtonWithTeamsList";