refactor(ui): read userRole from useAuthorized hook instead of prop drilling

Drop the userRole prop chain through GuardrailsPage → GuardrailsPanel →
TeamGuardrailsTab. Each component reads userRole directly from the
useAuthorized hook, matching the pattern used elsewhere in the dashboard.

Tests now mock useAuthorized per case (the same pattern as
top_key_view.test.tsx) instead of passing userRole as a prop.

Refs LIT-2494
This commit is contained in:
Ryan Crabbe 2026-05-09 09:26:54 -07:00
parent fe240e608e
commit 9e7aee5b76
No known key found for this signature in database
4 changed files with 31 additions and 14 deletions

View file

@ -4,9 +4,9 @@ import GuardrailsPanel from "@/components/guardrails";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
const GuardrailsPage = () => {
const { accessToken, userRole } = useAuthorized();
const { accessToken } = useAuthorized();
return <GuardrailsPanel accessToken={accessToken} userRole={userRole} />;
return <GuardrailsPanel accessToken={accessToken} />;
};
export default GuardrailsPage;

View file

@ -5,6 +5,7 @@ import { getGuardrailsList, deleteGuardrailCall } from "./networking";
import AddGuardrailForm from "./guardrails/add_guardrail_form";
import GuardrailTable from "./guardrails/guardrail_table";
import { isAdminRole } from "@/utils/roles";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import GuardrailInfoView from "./guardrails/guardrail_info";
import GuardrailTestPlayground from "./guardrails/GuardrailTestPlayground";
import NotificationsManager from "./molecules/notifications_manager";
@ -17,7 +18,6 @@ import { TeamGuardrailsTab } from "./guardrails/TeamGuardrailsTab";
interface GuardrailsPanelProps {
accessToken: string | null;
userRole?: string;
}
interface GuardrailItem {
@ -38,7 +38,8 @@ interface GuardrailsResponse {
guardrails: Guardrail[];
}
const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole }) => {
const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken }) => {
const { userRole } = useAuthorized();
const [guardrailsList, setGuardrailsList] = useState<Guardrail[]>([]);
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
const [isCustomCodeModalVisible, setIsCustomCodeModalVisible] = useState(false);
@ -252,7 +253,7 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
{
key: "submitted",
label: "Submitted Guardrails",
children: <TeamGuardrailsTab accessToken={accessToken} userRole={userRole} />,
children: <TeamGuardrailsTab accessToken={accessToken} />,
},
]}
/>

View file

@ -25,6 +25,7 @@ import {
import NotificationsManager from "@/components/molecules/notifications_manager";
import TeamDropdown from "@/components/common_components/team_dropdown";
import { useRegisterGuardrail } from "@/app/(dashboard)/hooks/guardrails/useRegisterGuardrail";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { isAdminRole } from "@/utils/roles";
type GuardrailStatus = "active" | "pending" | "rejected";
@ -805,10 +806,10 @@ function ConfirmDialog({
interface TeamGuardrailsTabProps {
accessToken: string | null;
userRole?: string;
}
export function TeamGuardrailsTab({ accessToken, userRole }: TeamGuardrailsTabProps) {
export function TeamGuardrailsTab({ accessToken }: TeamGuardrailsTabProps) {
const { userRole } = useAuthorized();
const isAdmin = userRole ? isAdminRole(userRole) : false;
const [guardrails, setGuardrails] = useState<TeamGuardrail[]>([]);
const [summary, setSummary] = useState({

View file

@ -1,6 +1,7 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderWithProviders, screen } from "./test-utils";
import { TeamGuardrailsTab } from "../src/components/guardrails/TeamGuardrailsTab";
import useAuthorized from "../src/app/(dashboard)/hooks/useAuthorized";
vi.mock("../src/components/networking", () => ({
listGuardrailSubmissions: vi.fn(),
@ -20,6 +21,10 @@ vi.mock("../src/components/common_components/team_dropdown", () => ({
default: () => null,
}));
vi.mock("../src/app/(dashboard)/hooks/useAuthorized", () => ({
default: vi.fn(),
}));
import { listGuardrailSubmissions } from "../src/components/networking";
const pendingSubmission = {
@ -37,7 +42,19 @@ const pendingSubmission = {
submitted_at: "2026-05-09T00:00:00Z",
};
const baseAuth = {
token: "test-token",
accessToken: "test-token",
userId: "user-1",
userEmail: "user@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
};
describe("TeamGuardrailsTab — approve/reject role gate", () => {
const mockUseAuthorized = vi.mocked(useAuthorized);
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(listGuardrailSubmissions).mockResolvedValue({
@ -47,11 +64,9 @@ describe("TeamGuardrailsTab — approve/reject role gate", () => {
});
it("hides Approve and Reject buttons for an internal user on a pending submission", async () => {
renderWithProviders(
<TeamGuardrailsTab accessToken="test-token" userRole="Internal User" />
);
mockUseAuthorized.mockReturnValue({ ...baseAuth, userRole: "Internal User" });
renderWithProviders(<TeamGuardrailsTab accessToken="test-token" />);
// Wait for the submission row to render
await screen.findByText("test-pending-guardrail");
expect(screen.queryByRole("button", { name: /approve/i })).not.toBeInTheDocument();
@ -59,9 +74,8 @@ describe("TeamGuardrailsTab — approve/reject role gate", () => {
});
it("shows Approve and Reject buttons for an admin on a pending submission", async () => {
renderWithProviders(
<TeamGuardrailsTab accessToken="test-token" userRole="Admin" />
);
mockUseAuthorized.mockReturnValue({ ...baseAuth, userRole: "Admin" });
renderWithProviders(<TeamGuardrailsTab accessToken="test-token" />);
await screen.findByText("test-pending-guardrail");
@ -70,6 +84,7 @@ describe("TeamGuardrailsTab — approve/reject role gate", () => {
});
it("hides Approve and Reject buttons when userRole is undefined (defaults to non-admin)", async () => {
mockUseAuthorized.mockReturnValue({ ...baseAuth, userRole: undefined });
renderWithProviders(<TeamGuardrailsTab accessToken="test-token" />);
await screen.findByText("test-pending-guardrail");