& {
+ children: ReactNode;
+ href: string;
+ }) => (
+
+ {children}
+
),
}));
@@ -64,7 +80,12 @@ vi.mock("@/components/shadcn", () => {
variant?: string;
size?: string;
asChild?: boolean;
- }) => ,
+ }) =>
+ _asChild && isValidElement(children) ? (
+ cloneElement(children, props)
+ ) : (
+
+ ),
InfoField: ({
children,
label,
@@ -386,6 +407,45 @@ const mockFinding: ResourceDrawerFinding = {
scan: null,
};
+describe("ResourceDetailDrawerContent — resource navigation", () => {
+ it("should render a View Resource link below the resource actions menu", () => {
+ // Given
+ render(
+ ,
+ );
+
+ // When
+ const viewResourceLink = screen.getByRole("link", {
+ name: "View Resource",
+ });
+ const resourceActionsMenu = screen.getByRole("menu", {
+ name: "Resource actions",
+ });
+
+ // Then
+ expect(viewResourceLink).toHaveAttribute(
+ "href",
+ "/resources?resourceId=res-1",
+ );
+ expect(viewResourceLink).toHaveAttribute("target", "_blank");
+ expect(viewResourceLink).toHaveAttribute("rel", "noopener noreferrer");
+ expect(
+ resourceActionsMenu.compareDocumentPosition(viewResourceLink) &
+ Node.DOCUMENT_POSITION_FOLLOWING,
+ ).not.toBe(0);
+ });
+});
const mockResourceRow: FindingResourceRow = {
id: "row-1",
rowType: "resource",
diff --git a/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx b/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx
index b1e986e635..367a3c98af 100644
--- a/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx
+++ b/ui/components/findings/table/resource-detail-drawer/resource-detail-drawer-content.tsx
@@ -299,6 +299,12 @@ function buildComplianceDetailHref({
return `/compliance/${encodeURIComponent(framework)}?${params.toString()}`;
}
+function buildResourceDetailHref(resourceId: string): string {
+ const params = new URLSearchParams();
+ params.set("resourceId", resourceId);
+ return `/resources?${params.toString()}`;
+}
+
interface ResourceDetailDrawerContentProps {
isLoading: boolean;
isNavigating: boolean;
@@ -416,6 +422,9 @@ export function ResourceDetailDrawerContent({
const nativeIacConfig = resolveNativeIacConfig(providerType);
const showOverviewCheckMetaContent = showCheckMetaContent;
const showOverviewFindingContent = Boolean(f);
+ const resourceDetailHref = f?.resourceId
+ ? buildResourceDetailHref(f.resourceId)
+ : null;
const overviewStatusExtended = f?.statusExtended;
const showOverviewStatusExtended = Boolean(overviewStatusExtended);
@@ -757,6 +766,21 @@ export function ResourceDetailDrawerContent({
)}
+
+ {resourceDetailHref && (
+
+
+
+ )}
>
)}