Fix RadixUI dropdown 1.0.0 (#4767)

* Add portal part

* Add portal part to pages

* Add portal part to pages
This commit is contained in:
Joe Au-Yeung
2022-09-30 12:29:22 +02:00
committed by GitHub
parent c78ea5de0b
commit 045ebb6e01
7 changed files with 301 additions and 276 deletions
+24 -17
View File
@@ -1,6 +1,11 @@
import React, { FC } from "react";
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import Dropdown, {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuPortal,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import Button from "@calcom/ui/v2/core/Button";
@@ -43,22 +48,24 @@ const DropdownActions = ({
) : (
<DropdownMenuTrigger asChild>{actionTrigger}</DropdownMenuTrigger>
)}
<DropdownMenuContent portalled>
{actions.map((action) => (
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none font-normal"
href={action.href}
StartIcon={action.icon}
onClick={action.onClick || defaultAction}
data-testid={action.id}>
{action.label}
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
<DropdownMenuPortal>
<DropdownMenuContent>
{actions.map((action) => (
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none font-normal"
href={action.href}
StartIcon={action.icon}
onClick={action.onClick || defaultAction}
data-testid={action.id}>
{action.label}
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
};
+80 -77
View File
@@ -24,6 +24,7 @@ import Dropdown, {
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
DropdownMenuPortal,
} from "@calcom/ui/Dropdown";
import EmptyScreen from "@calcom/ui/EmptyScreen";
import { Icon } from "@calcom/ui/Icon";
@@ -345,36 +346,70 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
<Button type="button" size="icon" color="minimal" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent portalled>
<DropdownMenuItem className="outline-none">
<Link href={calLink}>
<DropdownMenuPortal>
<DropdownMenuContent>
<DropdownMenuItem className="outline-none">
<Link href={calLink}>
<Button
rel="noreferrer"
target="_blank"
color="minimal"
size="sm"
StartIcon={Icon.FiExternalLink}
className="w-full rounded-none">
{t("preview") as string}
</Button>
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
rel="noreferrer"
target="_blank"
type="button"
color="minimal"
size="sm"
StartIcon={Icon.FiExternalLink}
className="w-full rounded-none">
{t("preview") as string}
className="w-full rounded-none text-left"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiClipboard}
onClick={() => {
navigator.clipboard.writeText(calLink);
showToast(t("link_copied"), "success");
}}>
{t("copy_link") as string}
</Button>
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
size="sm"
className="w-full rounded-none text-left"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiClipboard}
onClick={() => {
navigator.clipboard.writeText(calLink);
showToast(t("link_copied"), "success");
}}>
{t("copy_link") as string}
</Button>
</DropdownMenuItem>
{isNativeShare ? (
</DropdownMenuItem>
{isNativeShare ? (
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
size="sm"
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiUpload}
onClick={() => {
navigator
.share({
title: t("share"),
text: t("share_event"),
url: calLink,
})
.then(() => showToast(t("link_shared"), "success"))
.catch(() => showToast(t("failed"), "error"));
}}>
{t("share") as string}
</Button>
</DropdownMenuItem>
) : null}
<DropdownMenuItem className="outline-none">
<Button
type="button"
size="sm"
href={"/event-types/" + type.id}
color="minimal"
className="w-full rounded-none"
StartIcon={Icon.FiEdit2}>
{t("edit") as string}
</Button>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
@@ -382,59 +417,27 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
size="sm"
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiUpload}
onClick={() => {
navigator
.share({
title: t("share"),
text: t("share_event"),
url: calLink,
})
.then(() => showToast(t("link_shared"), "success"))
.catch(() => showToast(t("failed"), "error"));
}}>
{t("share") as string}
StartIcon={Icon.FiCopy}
onClick={() => openModal(group, type)}>
{t("duplicate") as string}
</Button>
</DropdownMenuItem>
) : null}
<DropdownMenuItem className="outline-none">
<Button
type="button"
size="sm"
href={"/event-types/" + type.id}
color="minimal"
className="w-full rounded-none"
StartIcon={Icon.FiEdit2}>
{t("edit") as string}
</Button>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
size="sm"
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiCopy}
onClick={() => openModal(group, type)}>
{t("duplicate") as string}
</Button>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem className="outline-none">
<Button
onClick={() => {
setDeleteDialogOpen(true);
setDeleteDialogTypeId(type.id);
}}
color="warn"
size="sm"
StartIcon={Icon.FiTrash}
className="w-full rounded-none">
{t("delete") as string}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem className="outline-none">
<Button
onClick={() => {
setDeleteDialogOpen(true);
setDeleteDialogTypeId(type.id);
}}
color="warn"
size="sm"
StartIcon={Icon.FiTrash}
className="w-full rounded-none">
{t("delete") as string}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
</div>
</div>
+76 -73
View File
@@ -18,6 +18,7 @@ import Dropdown, {
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
DropdownMenuPortal,
} from "@calcom/ui/v2/core/Dropdown";
import Shell from "@calcom/ui/v2/core/Shell";
import CreateEventTypeButton from "@calcom/ui/v2/modules/event-types/CreateEventType";
@@ -343,90 +344,92 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
<Button type="button" size="icon" color="secondary" StartIcon={Icon.FiMoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuContent portalled>
<DropdownMenuItem className="outline-none">
<Link href={calLink}>
<a target="_blank">
<DropdownMenuPortal>
<DropdownMenuContent>
<DropdownMenuItem className="outline-none">
<Link href={calLink}>
<a target="_blank">
<Button
color="minimal"
StartIcon={Icon.FiExternalLink}
className="w-full rounded-none">
{t("preview") as string}
</Button>
</a>
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none text-left"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiClipboard}
onClick={() => {
navigator.clipboard.writeText(calLink);
showToast(t("link_copied"), "success");
}}>
{t("copy_link") as string}
</Button>
</DropdownMenuItem>
{isNativeShare ? (
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
StartIcon={Icon.FiExternalLink}
className="w-full rounded-none">
{t("preview") as string}
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiUpload}
onClick={() => {
navigator
.share({
title: t("share"),
text: t("share_event"),
url: calLink,
})
.then(() => showToast(t("link_shared"), "success"))
.catch(() => showToast(t("failed"), "error"));
}}>
{t("share") as string}
</Button>
</a>
</Link>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none text-left"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiClipboard}
onClick={() => {
navigator.clipboard.writeText(calLink);
showToast(t("link_copied"), "success");
}}>
{t("copy_link") as string}
</Button>
</DropdownMenuItem>
{isNativeShare ? (
</DropdownMenuItem>
) : null}
<DropdownMenuItem className="outline-none">
<Button
type="button"
href={"/event-types/" + type.id}
color="minimal"
className="w-full"
StartIcon={Icon.FiEdit}>
{t("edit") as string}
</Button>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiUpload}
onClick={() => {
navigator
.share({
title: t("share"),
text: t("share_event"),
url: calLink,
})
.then(() => showToast(t("link_shared"), "success"))
.catch(() => showToast(t("failed"), "error"));
}}>
{t("share") as string}
StartIcon={Icon.FiCopy}
onClick={() => openModal(group, type)}>
{t("duplicate") as string}
</Button>
</DropdownMenuItem>
) : null}
<DropdownMenuItem className="outline-none">
<Button
type="button"
href={"/event-types/" + type.id}
color="minimal"
className="w-full"
StartIcon={Icon.FiEdit}>
{t("edit") as string}
</Button>
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<Button
type="button"
color="minimal"
className="w-full rounded-none"
data-testid={"event-type-duplicate-" + type.id}
StartIcon={Icon.FiCopy}
onClick={() => openModal(group, type)}>
{t("duplicate") as string}
</Button>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem className="outline-none">
<Button
onClick={() => {
setDeleteDialogOpen(true);
setDeleteDialogTypeId(type.id);
}}
color="destructive"
StartIcon={Icon.FiTrash}
className="w-full rounded-none">
{t("delete") as string}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem className="outline-none">
<Button
onClick={() => {
setDeleteDialogOpen(true);
setDeleteDialogTypeId(type.id);
}}
color="destructive"
StartIcon={Icon.FiTrash}
className="w-full rounded-none">
{t("delete") as string}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
</div>
</div>
+3 -2
View File
@@ -22,14 +22,15 @@ export const DropdownMenuTrigger = forwardRef<HTMLButtonElement, DropdownMenuTri
);
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
export const DropdownMenuTriggerItem = DropdownMenuPrimitive.TriggerItem;
export const DropdownMenuTriggerItem = DropdownMenuPrimitive.Trigger;
export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive["Content"]>;
export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuContentProps>(
({ children, ...props }, forwardedRef) => {
return (
<DropdownMenuPrimitive.Content
portalled={props.portalled}
{...props}
className={classNames(
`slideInTop w-50 relative z-10 mt-1 -ml-0 origin-top-right rounded-sm bg-white text-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none`,
+3 -2
View File
@@ -27,14 +27,15 @@ export const DropdownMenuTrigger = forwardRef<HTMLButtonElement, DropdownMenuTri
);
DropdownMenuTrigger.displayName = "DropdownMenuTrigger";
export const DropdownMenuTriggerItem = DropdownMenuPrimitive.TriggerItem;
export const DropdownMenuTriggerItem = DropdownMenuPrimitive.Trigger;
export const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
type DropdownMenuContentProps = ComponentProps<typeof DropdownMenuPrimitive["Content"]>;
export const DropdownMenuContent = forwardRef<HTMLDivElement, DropdownMenuContentProps>(
({ children, align = "end", ...props }, forwardedRef) => {
return (
<DropdownMenuPrimitive.Content
portalled={props.portalled}
align={align}
{...props}
className="shadow-dropdown w-50 relative z-10 mt-1 -ml-0 origin-top-right rounded-md border border-gray-200 bg-white text-sm"
+92 -89
View File
@@ -24,6 +24,7 @@ import Dropdown, {
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
DropdownMenuPortal,
} from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import Button from "@calcom/ui/v2/core/Button";
@@ -272,107 +273,109 @@ function UserDropdown({ small }: { small?: boolean }) {
)}
</button>
</DropdownMenuTrigger>
<DropdownMenuContent portalled={true} onInteractOutside={() => setMenuOpen(false)}>
{helpOpen ? (
<HelpMenuItem onHelpItemSelect={() => onHelpItemSelect()} />
) : (
<>
<DropdownMenuItem>
<button
onClick={() => {
mutation.mutate({ away: !user?.away });
utils.invalidateQueries("viewer.me");
}}
className="flex min-w-max cursor-pointer items-center px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
<Icon.FiMoon
className={classNames(
user.away
? "text-purple-500 group-hover:text-purple-700"
: "text-gray-500 group-hover:text-gray-700",
"h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-3"
)}
aria-hidden="true"
/>
{user.away ? t("set_as_free") : t("set_as_away")}
</button>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
{user.username && (
<DropdownMenuPortal>
<DropdownMenuContent onInteractOutside={() => setMenuOpen(false)}>
{helpOpen ? (
<HelpMenuItem onHelpItemSelect={() => onHelpItemSelect()} />
) : (
<>
<DropdownMenuItem>
<button
onClick={() => {
mutation.mutate({ away: !user?.away });
utils.invalidateQueries("viewer.me");
}}
className="flex min-w-max cursor-pointer items-center px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
<Icon.FiMoon
className={classNames(
user.away
? "text-purple-500 group-hover:text-purple-700"
: "text-gray-500 group-hover:text-gray-700",
"h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-3"
)}
aria-hidden="true"
/>
{user.away ? t("set_as_free") : t("set_as_away")}
</button>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
{user.username && (
<DropdownMenuItem>
<a
target="_blank"
rel="noopener noreferrer"
href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/${user.username}`}
className="flex items-center px-4 py-2 text-sm text-gray-700">
<Icon.FiExternalLink className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" />{" "}
{t("view_public_page")}
</a>
</DropdownMenuItem>
)}
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem>
<a
href={JOIN_SLACK}
target="_blank"
rel="noreferrer"
className="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900">
<Icon.FiSlack strokeWidth={1.5} className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" />{" "}
{t("join_our_slack")}
</a>
</DropdownMenuItem>
<DropdownMenuItem>
<a
target="_blank"
rel="noopener noreferrer"
href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/${user.username}`}
href={ROADMAP}
className="flex items-center px-4 py-2 text-sm text-gray-700">
<Icon.FiExternalLink className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" />{" "}
{t("view_public_page")}
<Icon.FiMap className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" /> {t("visit_roadmap")}
</a>
</DropdownMenuItem>
)}
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem>
<a
href={JOIN_SLACK}
target="_blank"
rel="noreferrer"
className="flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900">
<Icon.FiSlack strokeWidth={1.5} className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" />{" "}
{t("join_our_slack")}
</a>
</DropdownMenuItem>
<DropdownMenuItem>
<a
target="_blank"
rel="noopener noreferrer"
href={ROADMAP}
className="flex items-center px-4 py-2 text-sm text-gray-700">
<Icon.FiMap className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" /> {t("visit_roadmap")}
</a>
</DropdownMenuItem>
<button
className="flex w-full items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
onClick={() => setHelpOpen(true)}>
<Icon.FiHelpCircle
className={classNames(
"text-gray-500 group-hover:text-neutral-500",
"h-4 w-4 flex-shrink-0 ltr:mr-2"
)}
aria-hidden="true"
/>
{t("help")}
</button>
<DropdownMenuItem>
<a
target="_blank"
rel="noopener noreferrer"
href={DESKTOP_APP_LINK}
className="desktop-hidden hidden items-center px-4 py-2 text-sm text-gray-700 lg:flex">
<Icon.FiDownload className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" />{" "}
{t("download_desktop_app")}
</a>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem>
<a
onClick={() => signOut({ callbackUrl: "/auth/logout" })}
className="flex cursor-pointer items-center px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
<Icon.FiLogOut
<button
className="flex w-full items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
onClick={() => setHelpOpen(true)}>
<Icon.FiHelpCircle
className={classNames(
"text-gray-500 group-hover:text-gray-700",
"h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-3"
"text-gray-500 group-hover:text-neutral-500",
"h-4 w-4 flex-shrink-0 ltr:mr-2"
)}
aria-hidden="true"
/>
{t("sign_out")}
</a>
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
{t("help")}
</button>
<DropdownMenuItem>
<a
target="_blank"
rel="noopener noreferrer"
href={DESKTOP_APP_LINK}
className="desktop-hidden hidden items-center px-4 py-2 text-sm text-gray-700 lg:flex">
<Icon.FiDownload className="h-4 w-4 text-gray-500 ltr:mr-2 rtl:ml-3" />{" "}
{t("download_desktop_app")}
</a>
</DropdownMenuItem>
<DropdownMenuSeparator className="h-px bg-gray-200" />
<DropdownMenuItem>
<a
onClick={() => signOut({ callbackUrl: "/auth/logout" })}
className="flex cursor-pointer items-center px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900">
<Icon.FiLogOut
className={classNames(
"text-gray-500 group-hover:text-gray-700",
"h-4 w-4 flex-shrink-0 ltr:mr-2 rtl:ml-3"
)}
aria-hidden="true"
/>
{t("sign_out")}
</a>
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
}
+23 -16
View File
@@ -3,7 +3,12 @@ import React, { FC } from "react";
import { SVGComponent } from "@calcom/types/SVGComponent";
import { Icon } from "@calcom/ui/Icon";
import Button from "@calcom/ui/v2/core/Button";
import { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/v2/core/Dropdown";
import {
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuPortal,
} from "@calcom/ui/v2/core/Dropdown";
import Dropdown from "@calcom/ui/v2/core/Dropdown";
export type ActionType = {
@@ -45,21 +50,23 @@ const DropdownActions = ({
{actionTrigger}
</DropdownMenuTrigger>
)}
<DropdownMenuContent portalled>
{actions.map((action) => (
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
<Button
type="button"
color="minimal"
href={action.href}
StartIcon={action.icon}
onClick={action.onClick || defaultAction}
data-testid={action.id}>
{action.label}
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
<DropdownMenuPortal>
<DropdownMenuContent>
{actions.map((action) => (
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
<Button
type="button"
color="minimal"
href={action.href}
StartIcon={action.icon}
onClick={action.onClick || defaultAction}
data-testid={action.id}>
{action.label}
</Button>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenuPortal>
</Dropdown>
);
};