Merge pull request #19718 from BerriAI/litellm_ui_team_member_add_error

[Feature] UI - Virtual Keys: Auto Truncation of Table Values
This commit is contained in:
yuneng-jiang
2026-01-24 18:58:54 -08:00
committed by GitHub
2 changed files with 130 additions and 34 deletions
@@ -97,6 +97,7 @@ const mockKey: KeyResponse = {
litellm_budget_table: {},
organization_id: "org-1",
created_at: "2024-11-01T10:00:00Z",
created_by: "user-1",
updated_at: "2024-11-15T10:00:00Z",
team_spend: 5.5,
team_alias: "Test Team",
@@ -177,6 +178,7 @@ beforeEach(() => {
total_pages: 1,
} as KeysResponse,
isPending: false,
isFetching: false,
refetch: vi.fn(),
} as any);
@@ -265,6 +267,7 @@ it("should show skeleton loaders when isLoading is true", () => {
mockUseKeys.mockReturnValue({
data: null,
isPending: true,
isFetching: true,
refetch: vi.fn(),
} as any);
@@ -439,8 +442,8 @@ it("should open KeyInfoView when clicking on a key ID button", async () => {
// Verify table is visible before clicking - check for table-specific text
expect(screen.getByText(/Showing.*results/)).toBeInTheDocument();
// Find the key ID button (it should show the truncated token)
const keyIdButton = screen.getByText("sk-1234...");
// Find the key ID button (it shows the full token value, truncation is CSS-only)
const keyIdButton = screen.getByText("sk-1234567890abcdef");
expect(keyIdButton).toBeInTheDocument();
// Click on the key ID button
@@ -457,3 +460,85 @@ it("should open KeyInfoView when clicking on a key ID button", async () => {
// The "Showing X of Y results" text should not be visible when KeyInfoView is open
expect(screen.queryByText(/Showing.*results/)).not.toBeInTheDocument();
});
it("should display 'Default Proxy Admin' for user_id when value is 'default_user_id'", async () => {
const keyWithDefaultUserId = {
...mockKey,
user_id: "default_user_id",
};
mockUseFilterLogic.mockReturnValue({
filters: {
"Team ID": "",
"Organization ID": "",
"Key Alias": "",
"User ID": "",
"Sort By": "created_at",
"Sort Order": "desc",
},
filteredKeys: [keyWithDefaultUserId],
allKeyAliases: ["test-key-alias"],
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
handleFilterChange: vi.fn(),
handleFilterReset: vi.fn(),
});
const mockProps = {
teams: [mockTeam],
organizations: [mockOrganization],
onSortChange: vi.fn(),
currentSort: {
sortBy: "created_at",
sortOrder: "desc" as const,
},
};
renderWithProviders(<VirtualKeysTable {...mockProps} />);
await waitFor(() => {
expect(screen.getByText("Default Proxy Admin")).toBeInTheDocument();
});
});
it("should display 'Default Proxy Admin' for created_by when value is 'default_user_id'", async () => {
const keyWithDefaultCreatedBy = {
...mockKey,
created_by: "default_user_id",
};
mockUseFilterLogic.mockReturnValue({
filters: {
"Team ID": "",
"Organization ID": "",
"Key Alias": "",
"User ID": "",
"Sort By": "created_at",
"Sort Order": "desc",
},
filteredKeys: [keyWithDefaultCreatedBy],
allKeyAliases: ["test-key-alias"],
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
handleFilterChange: vi.fn(),
handleFilterReset: vi.fn(),
});
const mockProps = {
teams: [mockTeam],
organizations: [mockOrganization],
onSortChange: vi.fn(),
currentSort: {
sortBy: "created_at",
sortOrder: "desc" as const,
},
};
renderWithProviders(<VirtualKeysTable {...mockProps} />);
await waitFor(() => {
// The created_by column should display "Default Proxy Admin"
const defaultProxyAdminElements = screen.getAllByText("Default Proxy Admin");
expect(defaultProxyAdminElements.length).toBeGreaterThan(0);
});
});
@@ -129,22 +129,25 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
id: "token",
accessorKey: "token",
header: "Key ID",
size: 150,
size: 100,
enableSorting: true,
cell: (info) => (
<div className="overflow-hidden">
<Tooltip title={info.getValue() as string}>
cell: (info) => {
const value = info.getValue() as string;
const width = info.cell.column.getSize();
return (
<Tooltip title={value}>
<Button
size="xs"
variant="light"
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate max-w-[200px]"
className="font-mono text-blue-500 bg-blue-50 hover:bg-blue-100 text-xs font-normal px-2 py-0.5 text-left overflow-hidden truncate block"
style={{ maxWidth: width, overflow: "hidden" }}
onClick={() => setSelectedKey(info.row.original)}
>
{info.getValue() ? `${(info.getValue() as string).slice(0, 7)}...` : "-"}
{value ?? "-"}
</Button>
</Tooltip>
</div>
),
);
},
},
{
id: "key_alias",
@@ -188,13 +191,19 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
id: "team_id",
accessorKey: "team_id",
header: "Team ID",
size: 120,
size: 80,
enableSorting: false,
cell: (info) => (
<Tooltip title={info.getValue() as string}>
{info.getValue() ? `${(info.getValue() as string).slice(0, 7)}...` : "-"}
</Tooltip>
),
cell: (info) => {
const value = info.getValue() as string | null;
const width = info.cell.column.getSize();
return (
<Tooltip title={value}>
<span className={`font-mono text-xs truncate block`} style={{ maxWidth: width, overflow: "hidden" }}>
{value ?? "-"}
</span>
</Tooltip>
);
},
},
{
id: "organization_id",
@@ -227,18 +236,19 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
id: "user_id",
accessorKey: "user_id",
header: "User ID",
size: 120,
size: 70,
enableSorting: false,
cell: (info) => {
const userId = info.getValue() as string | null;
if (userId && userId.length > 15) {
return (
<Tooltip title={userId}>
<span>{userId.slice(0, 7)}...</span>
</Tooltip>
);
}
return userId ? userId : "-";
const displayValue = userId === "default_user_id" ? "Default Proxy Admin" : userId;
const width = info.cell.column.getSize();
return (
<Tooltip title={displayValue}>
<span className={`font-mono text-xs truncate block`} style={{ maxWidth: width, overflow: "hidden" }}>
{displayValue ?? "-"}
</span>
</Tooltip>
);
},
},
{
@@ -256,18 +266,19 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
id: "created_by",
accessorKey: "created_by",
header: "Created By",
size: 120,
size: 70,
enableSorting: false,
cell: (info) => {
const value = info.getValue() as string | null;
if (value && value.length > 15) {
return (
<Tooltip title={value}>
<span>{value.slice(0, 7)}...</span>
</Tooltip>
);
}
return value;
const displayValue = value === "default_user_id" ? "Default Proxy Admin" : value;
const width = info.cell.column.getSize();
return (
<Tooltip title={displayValue}>
<span className={`font-mono text-xs truncate block`} style={{ maxWidth: width, overflow: "hidden" }}>
{displayValue ?? "-"}
</span>
</Tooltip>
);
},
},
{