mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
revert(ui): keep the onboarding link modal on antd
The invitation dialog opens over the still-antd Invite User modal. Lifting only the shadcn dialog content above antd's mask leaves its own backdrop underneath, so an outside click reaches the wrong modal. Adding a second backdrop stops that but does not restore dismissal, and the same hazard already ships in three guardrails modals, so the stacking needs one shared fix rather than a fourth local workaround.
This commit is contained in:
parent
15a331f6df
commit
4544f7dbad
3 changed files with 42 additions and 117 deletions
|
|
@ -2848,6 +2848,9 @@
|
|||
"src/components/onboarding_link.tsx": {
|
||||
"local/filename-pascal-case": {
|
||||
"count": 1
|
||||
},
|
||||
"no-restricted-imports": {
|
||||
"count": 2
|
||||
}
|
||||
},
|
||||
"src/components/organisms/RegenerateKeyModal.tsx": {
|
||||
|
|
|
|||
|
|
@ -1,22 +1,5 @@
|
|||
import { describe, it, expect, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import OnboardingModal, { buildOnboardingUrl, InvitationLink } from "./onboarding_link";
|
||||
|
||||
vi.mock("./molecules/notifications_manager", () => ({ default: { success: vi.fn() } }));
|
||||
|
||||
const invitation: InvitationLink = {
|
||||
id: "inv-123",
|
||||
user_id: "user-abc",
|
||||
is_accepted: false,
|
||||
accepted_at: null,
|
||||
expires_at: new Date("2026-09-01"),
|
||||
created_at: new Date("2026-08-01"),
|
||||
created_by: "admin",
|
||||
updated_at: new Date("2026-08-01"),
|
||||
updated_by: "admin",
|
||||
has_user_setup_sso: false,
|
||||
};
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { buildOnboardingUrl } from "./onboarding_link";
|
||||
|
||||
describe("buildOnboardingUrl", () => {
|
||||
it("points the invitation link at the dedicated /ui/onboarding route", () => {
|
||||
|
|
@ -85,73 +68,3 @@ describe("buildOnboardingUrl", () => {
|
|||
).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("OnboardingModal", () => {
|
||||
it("renders nothing until it is opened", () => {
|
||||
render(
|
||||
<OnboardingModal
|
||||
isInvitationLinkModalVisible={false}
|
||||
setIsInvitationLinkModalVisible={vi.fn()}
|
||||
baseUrl="http://localhost:4000/"
|
||||
invitationLinkData={invitation}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText("http://localhost:4000/ui/onboarding?invitation_id=inv-123")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows the invitation url, the user id and an invitation-flavoured copy button", async () => {
|
||||
render(
|
||||
<OnboardingModal
|
||||
isInvitationLinkModalVisible
|
||||
setIsInvitationLinkModalVisible={vi.fn()}
|
||||
baseUrl="http://localhost:4000/"
|
||||
invitationLinkData={invitation}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(await screen.findByText("http://localhost:4000/ui/onboarding?invitation_id=inv-123")).toBeInTheDocument();
|
||||
expect(screen.getByText("user-abc")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("Invitation Link").length).toBeGreaterThan(0);
|
||||
expect(screen.getByRole("button", { name: "Copy invitation link" })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Copy and send the generated link to onboard this user/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("switches every label and the url to the reset-password flow", async () => {
|
||||
render(
|
||||
<OnboardingModal
|
||||
isInvitationLinkModalVisible
|
||||
setIsInvitationLinkModalVisible={vi.fn()}
|
||||
baseUrl="http://localhost:4000/"
|
||||
invitationLinkData={invitation}
|
||||
modalType="resetPassword"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByText("http://localhost:4000/ui/onboarding?invitation_id=inv-123&action=reset_password"),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getAllByText("Reset Password Link").length).toBeGreaterThan(0);
|
||||
expect(screen.getByRole("button", { name: "Copy password reset link" })).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Copy and send the generated link to the user to reset their password/),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("closes through setIsInvitationLinkModalVisible when the close control is used", async () => {
|
||||
const user = userEvent.setup();
|
||||
const setVisible = vi.fn();
|
||||
render(
|
||||
<OnboardingModal
|
||||
isInvitationLinkModalVisible
|
||||
setIsInvitationLinkModalVisible={setVisible}
|
||||
baseUrl="http://localhost:4000/"
|
||||
invitationLinkData={invitation}
|
||||
/>,
|
||||
);
|
||||
|
||||
await user.click(await screen.findByRole("button", { name: /close/i }));
|
||||
|
||||
expect(setVisible).toHaveBeenCalledWith(false);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import { Button, Modal, Typography } from "antd";
|
||||
import { CopyToClipboard } from "react-copy-to-clipboard";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import { Text } from "@tremor/react";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
|
||||
export interface InvitationLink {
|
||||
|
|
@ -58,7 +58,10 @@ export default function OnboardingModal({
|
|||
invitationLinkData,
|
||||
modalType = "invitation",
|
||||
}: OnboardingProps) {
|
||||
const linkLabel = modalType === "invitation" ? "Invitation Link" : "Reset Password Link";
|
||||
const { Paragraph } = Typography;
|
||||
const handleInvitationOk = () => {
|
||||
setIsInvitationLinkModalVisible(false);
|
||||
};
|
||||
|
||||
const handleInvitationCancel = () => {
|
||||
setIsInvitationLinkModalVisible(false);
|
||||
|
|
@ -73,30 +76,36 @@ export default function OnboardingModal({
|
|||
});
|
||||
|
||||
return (
|
||||
<Dialog open={isInvitationLinkModalVisible} onOpenChange={(open) => !open && handleInvitationCancel()}>
|
||||
<DialogContent className="z-[1100] max-h-[calc(100dvh-2rem)] overflow-y-auto sm:max-w-[800px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{linkLabel}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<p>
|
||||
{modalType === "invitation"
|
||||
? "Copy and send the generated link to onboard this user to the proxy."
|
||||
: "Copy and send the generated link to the user to reset their password."}
|
||||
</p>
|
||||
<div className="flex justify-between pt-5 pb-2">
|
||||
<span className="text-base">User ID</span>
|
||||
<span className="min-w-0 break-words">{invitationLinkData?.user_id}</span>
|
||||
</div>
|
||||
<div className="flex justify-between pt-5 pb-2">
|
||||
<span>{linkLabel}</span>
|
||||
<span className="min-w-0 break-words">{getInvitationUrl()}</span>
|
||||
</div>
|
||||
<div className="flex justify-end mt-5">
|
||||
<CopyToClipboard text={getInvitationUrl()} onCopy={() => NotificationsManager.success("Copied!")}>
|
||||
<Button>{modalType === "invitation" ? "Copy invitation link" : "Copy password reset link"}</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Modal
|
||||
title={modalType === "invitation" ? "Invitation Link" : "Reset Password Link"}
|
||||
open={isInvitationLinkModalVisible}
|
||||
width={800}
|
||||
footer={null}
|
||||
onOk={handleInvitationOk}
|
||||
onCancel={handleInvitationCancel}
|
||||
>
|
||||
<Paragraph>
|
||||
{modalType === "invitation"
|
||||
? "Copy and send the generated link to onboard this user to the proxy."
|
||||
: "Copy and send the generated link to the user to reset their password."}
|
||||
</Paragraph>
|
||||
<div className="flex justify-between pt-5 pb-2">
|
||||
<Text className="text-base">User ID</Text>
|
||||
<Text>{invitationLinkData?.user_id}</Text>
|
||||
</div>
|
||||
<div className="flex justify-between pt-5 pb-2">
|
||||
<Text>{modalType === "invitation" ? "Invitation Link" : "Reset Password Link"}</Text>
|
||||
<Text>
|
||||
<Text>{getInvitationUrl()}</Text>
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex justify-end mt-5">
|
||||
<CopyToClipboard text={getInvitationUrl()} onCopy={() => NotificationsManager.success("Copied!")}>
|
||||
<Button type="primary">
|
||||
{modalType === "invitation" ? "Copy invitation link" : "Copy password reset link"}
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue