Ensure unique keys in navbar menu items (#16987)

This commit is contained in:
yuneng-jiang 2025-11-22 19:20:01 -08:00 • committed by GitHub
parent f3cd0b0bc4
commit 16a5178527
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 66 additions and 32 deletions

View file

@ -407,7 +407,7 @@ export default function CreateKeyPage() {
/>
) : page == "api_ref" ? (
<APIReferenceView proxySettings={proxySettings} />
) : page == "settings" ? (
) : page == "logging-and-alerts" ? (
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
) : page == "budgets" ? (
<BudgetPanel accessToken={accessToken} />
@ -419,7 +419,7 @@ export default function CreateKeyPage() {
<PromptsPanel accessToken={accessToken} userRole={userRole} />
) : page == "transform-request" ? (
<TransformRequestPanel accessToken={accessToken} />
) : page == "general-settings" ? (
) : page == "router-settings" ? (
<GeneralSettings
userID={userID}
userRole={userRole}
@ -428,7 +428,7 @@ export default function CreateKeyPage() {
/>
) : page == "ui-theme" ? (
<UIThemeSettings userID={userID} userRole={userRole} accessToken={accessToken} />
) : page == "cost-tracking-settings" ? (
) : page == "cost-tracking" ? (
<CostTrackingSettings userID={userID} userRole={userRole} accessToken={accessToken} />
) : page == "model-hub-table" ? (
isAdminRole(userRole) ? (

View file

@ -66,4 +66,33 @@ describe("Sidebar (leftnav)", () => {
expect(getByText("Search Tools")).toBeInTheDocument();
});
});
it("has no duplicate keys among all menu items and their children", () => {
// Helper to recursively extract all keys from Ant Design Menu items
function getAllKeysFromMenu(wrapper: HTMLElement): string[] {
const allKeys: string[] = [];
// Ant Design renders key as data-menu-id or inside attributes, but for this case, we look for text as fallback.
// For a generic check, here we fetch ids from rendered list items, and also descend into submenus
const items = wrapper.querySelectorAll("[data-menu-id]");
items.forEach((item) => {
const dataMenuId = item.getAttribute("data-menu-id");
if (dataMenuId) {
allKeys.push(dataMenuId);
}
});
return allKeys;
}
const { container } = render(<Sidebar {...defaultProps} />);
const allRenderedKeys = getAllKeysFromMenu(container);
const keySet = new Set<string>();
const duplicates: string[] = [];
for (const key of allRenderedKeys) {
if (keySet.has(key)) {
duplicates.push(key);
}
keySet.add(key);
}
expect(duplicates).toHaveLength(0);
});
});

View file

@ -49,84 +49,89 @@ const Sidebar: React.FC<SidebarProps> = ({ accessToken, setPage, userRole, defau
// Note: If a menu item does not have a role, it is visible to all roles.
const menuItems: MenuItem[] = [
{
key: "1",
key: "api-keys",
page: "api-keys",
label: "Virtual Keys",
icon: <KeyOutlined style={{ fontSize: "18px" }} />,
},
{
key: "3",
key: "llm-playground",
page: "llm-playground",
label: "Playground",
icon: <PlayCircleOutlined style={{ fontSize: "18px" }} />,
roles: rolesWithWriteAccess,
},
{
key: "2",
key: "models",
page: "models",
label: "Models + Endpoints",
icon: <BlockOutlined style={{ fontSize: "18px" }} />,
roles: rolesWithWriteAccess,
},
{
key: "12",
key: "new_usage",
page: "new_usage",
label: "Usage",
icon: <BarChartOutlined style={{ fontSize: "18px" }} />,
roles: [...all_admin_roles, ...internalUserRoles],
},
{ key: "6", page: "teams", label: "Teams", icon: <TeamOutlined style={{ fontSize: "18px" }} /> },
{ key: "teams", page: "teams", label: "Teams", icon: <TeamOutlined style={{ fontSize: "18px" }} /> },
{
key: "17",
key: "organizations",
page: "organizations",
label: "Organizations",
icon: <BankOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "5",
key: "users",
page: "users",
label: "Internal Users",
icon: <UserOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "10",
key: "budgets",
page: "budgets",
label: "Budgets",
icon: <CreditCardOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{ key: "14", page: "api_ref", label: "API Reference", icon: <ApiOutlined style={{ fontSize: "18px" }} /> },
{ key: "api_ref", page: "api_ref", label: "API Reference", icon: <ApiOutlined style={{ fontSize: "18px" }} /> },
{
key: "16",
key: "model-hub-table",
page: "model-hub-table",
label: "AI Hub",
icon: <AppstoreOutlined style={{ fontSize: "18px" }} />,
},
{ key: "15", page: "logs", label: "Logs", icon: <LineChartOutlined style={{ fontSize: "18px" }} /> },
{ key: "logs", page: "logs", label: "Logs", icon: <LineChartOutlined style={{ fontSize: "18px" }} /> },
{
key: "11",
key: "guardrails",
page: "guardrails",
label: "Guardrails",
icon: <SafetyOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{ key: "18", page: "mcp-servers", label: "MCP Servers", icon: <ToolOutlined style={{ fontSize: "18px" }} /> },
{
key: "26",
key: "mcp-servers",
page: "mcp-servers",
label: "MCP Servers",
icon: <ToolOutlined style={{ fontSize: "18px" }} />,
},
{
key: "tools",
page: "tools",
label: "Tools",
icon: <ToolOutlined style={{ fontSize: "18px" }} />,
children: [
{
key: "28",
key: "search-tools",
page: "search-tools",
label: "Search Tools",
icon: <SearchOutlined style={{ fontSize: "18px" }} />,
},
{
key: "21",
key: "vector-stores",
page: "vector-stores",
label: "Vector Stores",
icon: <DatabaseOutlined style={{ fontSize: "18px" }} />,
@ -141,35 +146,35 @@ const Sidebar: React.FC<SidebarProps> = ({ accessToken, setPage, userRole, defau
icon: <ExperimentOutlined style={{ fontSize: "18px" }} />,
children: [
{
key: "9",
key: "caching",
page: "caching",
label: "Caching",
icon: <DatabaseOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "29",
key: "agents",
page: "agents",
label: "Agents",
icon: <RobotOutlined style={{ fontSize: "18px" }} />,
roles: rolesWithWriteAccess,
},
{
key: "25",
key: "prompts",
page: "prompts",
label: "Prompts",
icon: <FileTextOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "20",
key: "transform-request",
page: "transform-request",
label: "API Playground",
icon: <ApiOutlined style={{ fontSize: "18px" }} />,
roles: [...all_admin_roles, ...internalUserRoles],
},
{
key: "19",
key: "tag-management",
page: "tag-management",
label: "Tag Management",
icon: <TagsOutlined style={{ fontSize: "18px" }} />,
@ -186,35 +191,35 @@ const Sidebar: React.FC<SidebarProps> = ({ accessToken, setPage, userRole, defau
roles: all_admin_roles,
children: [
{
key: "11",
page: "general-settings",
key: "router-settings",
page: "router-settings",
label: "Router Settings",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "8",
page: "settings",
key: "logging-and-alerts",
page: "logging-and-alerts",
label: "Logging & Alerts",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "13",
key: "admin-panel",
page: "admin-panel",
label: "Admin Settings",
icon: <SettingOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "27",
page: "cost-tracking-settings",
key: "cost-tracking",
page: "cost-tracking",
label: "Cost Tracking",
icon: <BarChartOutlined style={{ fontSize: "18px" }} />,
roles: all_admin_roles,
},
{
key: "14",
key: "ui-theme",
page: "ui-theme",
label: "UI Theme",
icon: <BgColorsOutlined style={{ fontSize: "18px" }} />,