mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
chore(ui): make View Resource an icon-only link next to the resource name (#11193)
Co-authored-by: alejandrobailo <alejandrobailo94@gmail.com>
This commit is contained in:
committed by
GitHub
parent
bfcbe0a9c4
commit
298ad3382f
@@ -16,6 +16,7 @@ All notable changes to the **Prowler UI** are documented in this file.
|
||||
- Lighthouse now accepts Prowler App Finding Groups MCP tools [(#11140)](https://github.com/prowler-cloud/prowler/pull/11140)
|
||||
- Attack Paths graph now uses React Flow with improved layout, interactions, export, minimap, and browser test coverage [(#10686)](https://github.com/prowler-cloud/prowler/pull/10686)
|
||||
- SAML ACS URL is only shown if the email domain is configured [(#11144)](https://github.com/prowler-cloud/prowler/pull/11144)
|
||||
- "View Resource" action in the finding resource detail drawer is now an icon-only link rendered next to the resource name (instead of a text button in the UID row), keeping the "View in AWS Console" link unchanged [(#11193)](https://github.com/prowler-cloud/prowler/pull/11193)
|
||||
|
||||
### 🐞 Fixed
|
||||
|
||||
|
||||
+24
-3
@@ -46,10 +46,12 @@ vi.mock("next/link", () => ({
|
||||
default: ({
|
||||
children,
|
||||
href,
|
||||
prefetch: _prefetch,
|
||||
...props
|
||||
}: AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||
children: ReactNode;
|
||||
href: string;
|
||||
prefetch?: boolean;
|
||||
}) => (
|
||||
<a href={href} {...props}>
|
||||
{children}
|
||||
@@ -288,8 +290,21 @@ vi.mock("@/components/ui/entities/date-with-time", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ui/entities/entity-info", () => ({
|
||||
EntityInfo: ({ idAction }: { idAction?: ReactNode }) =>
|
||||
idAction ? <span data-testid="entity-id-action">{idAction}</span> : null,
|
||||
EntityInfo: ({
|
||||
nameAction,
|
||||
idAction,
|
||||
}: {
|
||||
nameAction?: ReactNode;
|
||||
idAction?: ReactNode;
|
||||
}) =>
|
||||
nameAction || idAction ? (
|
||||
<span>
|
||||
{nameAction && (
|
||||
<span data-testid="entity-name-action">{nameAction}</span>
|
||||
)}
|
||||
{idAction && <span data-testid="entity-id-action">{idAction}</span>}
|
||||
</span>
|
||||
) : null,
|
||||
}));
|
||||
|
||||
vi.mock("@/components/ui/table", () => ({
|
||||
@@ -428,7 +443,7 @@ const mockFinding: ResourceDrawerFinding = {
|
||||
};
|
||||
|
||||
describe("ResourceDetailDrawerContent — resource navigation", () => {
|
||||
it("should render a View Resource link inline next to the resource UID", () => {
|
||||
it("should render an icon-only View Resource link next to the resource name", () => {
|
||||
// Given
|
||||
render(
|
||||
<ResourceDetailDrawerContent
|
||||
@@ -457,6 +472,12 @@ describe("ResourceDetailDrawerContent — resource navigation", () => {
|
||||
);
|
||||
expect(viewResourceLink).toHaveAttribute("target", "_blank");
|
||||
expect(viewResourceLink).toHaveAttribute("rel", "noopener noreferrer");
|
||||
// Icon-only: accessible name comes from an sr-only span, not from an
|
||||
// aria-label attribute, so the text lives in the DOM (more semantic).
|
||||
expect(viewResourceLink).toHaveAccessibleName("View Resource");
|
||||
expect(viewResourceLink).not.toHaveAttribute("aria-label");
|
||||
const srOnlyLabel = viewResourceLink.querySelector(".sr-only");
|
||||
expect(srOnlyLabel).toHaveTextContent("View Resource");
|
||||
});
|
||||
});
|
||||
const mockResourceRow: FindingResourceRow = {
|
||||
|
||||
+28
-20
@@ -441,8 +441,7 @@ export function ResourceDetailDrawerContent({
|
||||
findingUid: f?.uid,
|
||||
region: resourceRegion,
|
||||
});
|
||||
const hasIdAction =
|
||||
Boolean(resourceDetailHref) || Boolean(externalResourceTarget);
|
||||
const hasIdAction = Boolean(externalResourceTarget);
|
||||
const findingRecommendationUrl = f?.remediation.recommendation.url;
|
||||
const checkRecommendationUrl = checkMeta.remediation.recommendation.url;
|
||||
const recommendationUrl = isNonEmptyString(findingRecommendationUrl)
|
||||
@@ -712,32 +711,41 @@ export function ResourceDetailDrawerContent({
|
||||
entityAlias={resourceName}
|
||||
entityId={resourceUid}
|
||||
idLabel="UID"
|
||||
idAction={
|
||||
hasIdAction ? (
|
||||
<span className="inline-flex items-center gap-2">
|
||||
{resourceDetailHref && (
|
||||
nameAction={
|
||||
resourceDetailHref ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button variant="link" size="link-sm" asChild>
|
||||
<Link
|
||||
href={resourceDetailHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
prefetch={false}
|
||||
>
|
||||
View Resource
|
||||
<ExternalLink className="size-3" />
|
||||
<span className="sr-only">View Resource</span>
|
||||
<ExternalLink
|
||||
className="size-3"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{externalResourceTarget && (
|
||||
<ExternalResourceLink
|
||||
providerType={providerType}
|
||||
resourceUid={resourceUid}
|
||||
providerUid={providerUid}
|
||||
resourceName={resourceName}
|
||||
findingUid={f?.uid}
|
||||
region={resourceRegion}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
View Resource
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : undefined
|
||||
}
|
||||
idAction={
|
||||
hasIdAction ? (
|
||||
<ExternalResourceLink
|
||||
providerType={providerType}
|
||||
resourceUid={resourceUid}
|
||||
providerUid={providerUid}
|
||||
resourceName={resourceName}
|
||||
findingUid={f?.uid}
|
||||
region={resourceRegion}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -17,6 +17,8 @@ interface EntityInfoProps {
|
||||
icon?: ReactNode;
|
||||
/** Small icon rendered inline before the entity alias text */
|
||||
nameIcon?: ReactNode;
|
||||
/** Inline element rendered after the entity alias (e.g. action link). */
|
||||
nameAction?: ReactNode;
|
||||
entityAlias?: string;
|
||||
entityId?: string;
|
||||
badge?: string;
|
||||
@@ -37,6 +39,7 @@ export const EntityInfo = ({
|
||||
cloudProvider,
|
||||
icon,
|
||||
nameIcon,
|
||||
nameAction,
|
||||
entityAlias,
|
||||
entityId,
|
||||
badge,
|
||||
@@ -74,6 +77,7 @@ export const EntityInfo = ({
|
||||
({badge})
|
||||
</span>
|
||||
)}
|
||||
{nameAction && <span className="shrink-0">{nameAction}</span>}
|
||||
</div>
|
||||
{entityId && (
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
|
||||
Reference in New Issue
Block a user