mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Merge pull request #39108 from yatishgoel/bugfix/form-field-checkbox-width
fix(ui): stop checkboxes stretching to the full width of a form field
This commit is contained in:
commit
720c1fa28c
6 changed files with 84 additions and 3 deletions
|
|
@ -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(),
|
||||
|
|
@ -294,6 +295,20 @@ describe("CreateUserButton", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("lays the send invitation email checkbox out beside its label", async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: PointerEventsCheckLevel.Never });
|
||||
renderWithProviders(<CreateUserButton {...defaultProps} />);
|
||||
|
||||
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 });
|
||||
|
||||
expectControlBesideLabel(within(dialog).getByRole("checkbox"));
|
||||
});
|
||||
|
||||
describe("organizations", () => {
|
||||
it("should send organizations list in POST body when organizations are selected", async () => {
|
||||
const { useOrganizations } = await import("@/app/(dashboard)/hooks/organizations/useOrganizations");
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ export const CreateUserButton: React.FC<CreateuserProps> = ({
|
|||
);
|
||||
|
||||
const sendInviteEmailField = (
|
||||
<FormField control={form.control} name="send_invite_email" label="Send invitation email">
|
||||
<FormField control={form.control} name="send_invite_email" label="Send invitation email" orientation="horizontal">
|
||||
{({ id, value, onChange, onBlur }) => (
|
||||
<Checkbox id={id} checked={value} onCheckedChange={onChange} onBlur={onBlur} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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 <BaseSSOSettingsForm form={form} onFormSubmit={vi.fn()} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
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 <BaseSSOSettingsForm form={form} onFormSubmit={vi.fn()} />;
|
||||
};
|
||||
|
||||
renderWithProviders(<TestWrapper />);
|
||||
|
||||
await openProviderDropdown();
|
||||
await user().click(await screen.findByText(/okta/i));
|
||||
|
||||
expectControlBesideLabel(await screen.findByRole("checkbox", { name: label }));
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderProviderFields", () => {
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ const SSOProviderField = ({ field }: { field: SSOProviderConfig["fields"][number
|
|||
|
||||
if (field.type === "checkbox") {
|
||||
return (
|
||||
<FormField control={control} name={field.name} label={field.label}>
|
||||
<FormField control={control} name={field.name} label={field.label} orientation="horizontal">
|
||||
{({ value, onChange, onBlur, id, ...rest }) => (
|
||||
<Checkbox
|
||||
id={id}
|
||||
|
|
@ -413,7 +413,7 @@ export const MappingToggleField = ({
|
|||
const { control } = useFormContext<SSOSettingsFormValues>();
|
||||
|
||||
return (
|
||||
<FormField control={control} name={name} label={label}>
|
||||
<FormField control={control} name={name} label={label} orientation="horizontal">
|
||||
{({ value, onChange, onBlur, id, ...rest }) => (
|
||||
<Checkbox
|
||||
id={id}
|
||||
|
|
|
|||
|
|
@ -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(<Field />);
|
||||
|
||||
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(<Field orientation="horizontal" />);
|
||||
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", () => {
|
||||
|
|
|
|||
18
ui/litellm-dashboard/tests/fieldOrientation.ts
Normal file
18
ui/litellm-dashboard/tests/fieldOrientation.ts
Normal file
|
|
@ -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);
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue