From 054acb2223cb4e4a0c8c2a2b57fc0ad00a854fa5 Mon Sep 17 00:00:00 2001 From: yatishgoel Date: Tue, 1 Sep 2026 17:05:08 +0530 Subject: [PATCH 1/2] fix(ui): stop checkboxes stretching to the full width of a form field --- .../src/components/CreateUserButton.test.tsx | 15 +++++++++++++++ .../src/components/CreateUserButton.tsx | 2 +- .../SSOSettings/Modals/BaseSSOSettingsForm.tsx | 4 ++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx b/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx index 3777c46973f..46c66ee9ef6 100644 --- a/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx +++ b/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx @@ -294,6 +294,21 @@ describe("CreateUserButton", () => { }); }); + it("lays the send invitation email checkbox out beside its label", async () => { + const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never }); + renderWithProviders(); + + await waitFor(() => { + expect(screen.getByRole("button", { name: /\+ invite user/i })).toBeInTheDocument(); + }); + await user.click(screen.getByRole("button", { name: /\+ invite user/i })); + + const dialog = screen.getByRole("dialog", { name: /invite user/i }); + const checkbox = within(dialog).getByRole("checkbox"); + + expect(checkbox.closest('[data-slot="field"]')).toHaveAttribute("data-orientation", "horizontal"); + }); + describe("organizations", () => { it("should send organizations list in POST body when organizations are selected", async () => { const { useOrganizations } = await import("@/app/(dashboard)/hooks/organizations/useOrganizations"); diff --git a/ui/litellm-dashboard/src/components/CreateUserButton.tsx b/ui/litellm-dashboard/src/components/CreateUserButton.tsx index e052a9e0818..0f7c356b8cc 100644 --- a/ui/litellm-dashboard/src/components/CreateUserButton.tsx +++ b/ui/litellm-dashboard/src/components/CreateUserButton.tsx @@ -270,7 +270,7 @@ export const CreateUserButton: React.FC = ({ ); const sendInviteEmailField = ( - + {({ id, value, onChange, onBlur }) => ( )} diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx index 5216da382d0..cb97304f77a 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.tsx @@ -303,7 +303,7 @@ const SSOProviderField = ({ field }: { field: SSOProviderConfig["fields"][number if (field.type === "checkbox") { return ( - + {({ value, onChange, onBlur, id, ...rest }) => ( (); return ( - + {({ value, onChange, onBlur, id, ...rest }) => ( Date: Tue, 1 Sep 2026 13:54:54 -0400 Subject: [PATCH 2/2] test(ui): assert the resolved checkbox layout, not just data-orientation The invite-dialog test only read data-orientation, so a regression inside the shared field variants could restore the full-width bar and still pass. Assert the classes that carry the layout, cover the two SSO call sites the fix also changed, and pin the vertical/horizontal contract on the primitive. --- .../src/components/CreateUserButton.test.tsx | 4 +-- .../Modals/BaseSSOSettingsForm.test.tsx | 33 +++++++++++++++++++ .../src/components/ui/field.test.tsx | 15 +++++++++ .../tests/fieldOrientation.ts | 18 ++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 ui/litellm-dashboard/tests/fieldOrientation.ts diff --git a/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx b/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx index 46c66ee9ef6..68bfd94bd63 100644 --- a/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx +++ b/ui/litellm-dashboard/src/components/CreateUserButton.test.tsx @@ -5,6 +5,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { CreateUserButton } from "./CreateUserButton"; import * as networking from "./networking"; import { toast } from "@/lib/toast"; +import { expectControlBesideLabel } from "../../tests/fieldOrientation"; vi.mock("./networking", () => ({ userCreateCall: vi.fn(), @@ -304,9 +305,8 @@ describe("CreateUserButton", () => { await user.click(screen.getByRole("button", { name: /\+ invite user/i })); const dialog = screen.getByRole("dialog", { name: /invite user/i }); - const checkbox = within(dialog).getByRole("checkbox"); - expect(checkbox.closest('[data-slot="field"]')).toHaveAttribute("data-orientation", "horizontal"); + expectControlBesideLabel(within(dialog).getByRole("checkbox")); }); describe("organizations", () => { diff --git a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx index 54566e1d75a..eddc5716305 100644 --- a/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx +++ b/ui/litellm-dashboard/src/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm.test.tsx @@ -9,6 +9,7 @@ import BaseSSOSettingsForm, { submitMountedSSOValues, useSSOSettingsForm, } from "./BaseSSOSettingsForm"; +import { expectControlBesideLabel } from "../../../../../../tests/fieldOrientation"; const user = () => userEvent.setup({ pointerEventsCheck: 0 }); @@ -233,6 +234,38 @@ describe("BaseSSOSettingsForm", () => { expect(screen.queryByText("Use Team Mappings")).not.toBeInTheDocument(); }); + + it("lays a provider checkbox field out beside its label", async () => { + const TestWrapper = () => { + const form = useSSOSettingsForm("sso-settings"); + + return ; + }; + + renderWithProviders(); + + await openProviderDropdown(); + await user().click(await screen.findByText(/saml sso/i)); + + expectControlBesideLabel( + await screen.findByRole("checkbox", { name: "Allow IdP-initiated (unsolicited) responses" }), + ); + }); + + it.each(["Use Role Mappings", "Use Team Mappings"])("lays the %s toggle out beside its label", async (label) => { + const TestWrapper = () => { + const form = useSSOSettingsForm("sso-settings"); + + return ; + }; + + renderWithProviders(); + + await openProviderDropdown(); + await user().click(await screen.findByText(/okta/i)); + + expectControlBesideLabel(await screen.findByRole("checkbox", { name: label })); + }); }); describe("renderProviderFields", () => { diff --git a/ui/litellm-dashboard/src/components/ui/field.test.tsx b/ui/litellm-dashboard/src/components/ui/field.test.tsx index 4bb3d0ec665..5765a637526 100644 --- a/ui/litellm-dashboard/src/components/ui/field.test.tsx +++ b/ui/litellm-dashboard/src/components/ui/field.test.tsx @@ -14,6 +14,7 @@ import { FieldSet, FieldTitle, } from "./field"; +import { ROW_LAYOUT_CLASSES, STRETCH_CHILDREN_CLASS } from "../../../tests/fieldOrientation"; describe("FieldError", () => { it("renders nothing when there are no errors and no children", () => { @@ -85,6 +86,20 @@ describe("Field", () => { expect(screen.getByRole("group")).toHaveAttribute("data-orientation", "horizontal"); }); + + it("stretches every child when vertical, which is what inputs, selects and textareas want", () => { + render(); + + expect(screen.getByRole("group")).toHaveClass("flex-col", STRETCH_CHILDREN_CLASS); + }); + + it("lays children in a row at their own width when horizontal, so a checkbox stays square", () => { + render(); + const field = screen.getByRole("group"); + + expect(field).toHaveClass(...ROW_LAYOUT_CLASSES); + expect(field).not.toHaveClass(STRETCH_CHILDREN_CLASS); + }); }); describe("field primitives forward refs to their DOM node", () => { diff --git a/ui/litellm-dashboard/tests/fieldOrientation.ts b/ui/litellm-dashboard/tests/fieldOrientation.ts new file mode 100644 index 00000000000..49ba5056d66 --- /dev/null +++ b/ui/litellm-dashboard/tests/fieldOrientation.ts @@ -0,0 +1,18 @@ +import { expect } from "vitest"; + +export const ROW_LAYOUT_CLASSES = ["flex-row", "items-center"] as const; +export const STRETCH_CHILDREN_CLASS = "*:w-full"; + +/** + * Asserts a control sits beside its label at its own width instead of being stretched across the + * field. Reaches for the resolved classes because the defect is purely visual: nothing accessible + * distinguishes a square checkbox from a full-width bar. + */ +export const expectControlBesideLabel = (control: HTMLElement): void => { + const field = control.closest('[data-slot="field"]'); + if (field === null) throw new Error("control is not rendered inside a form field"); + + expect(field).toHaveAttribute("data-orientation", "horizontal"); + expect(field).toHaveClass(...ROW_LAYOUT_CLASSES); + expect(field).not.toHaveClass(STRETCH_CHILDREN_CLASS); +};