From 22f79edec50bbbd33f4e0c66bb183f3eb805b19e Mon Sep 17 00:00:00 2001 From: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:31:39 +0100 Subject: [PATCH] refactor(ui): replace HeroUI Snippet with CodeSnippet component (#10319) --- .../integrations/saml/saml-config-form.tsx | 16 +++--- .../invitations/invitation-details.tsx | 17 +------ ui/components/scans/table/scan-detail.tsx | 14 +++-- .../ui/code-snippet/code-snippet.tsx | 31 +++++++---- ui/components/ui/entities/index.ts | 1 - ui/components/ui/entities/snippet-chip.tsx | 51 ------------------- .../ui/table/data-table-pagination.tsx | 2 +- .../users/profile/api-key-success-modal.tsx | 21 +++----- .../users/profile/membership-item.tsx | 4 +- .../users/profile/revoke-api-key-modal.tsx | 18 +++---- ui/components/users/profile/role-item.tsx | 1 + .../users/profile/user-basic-info-card.tsx | 5 +- 12 files changed, 61 insertions(+), 120 deletions(-) delete mode 100644 ui/components/ui/entities/snippet-chip.tsx diff --git a/ui/components/integrations/saml/saml-config-form.tsx b/ui/components/integrations/saml/saml-config-form.tsx index d52e4e4ba7..daeb301832 100644 --- a/ui/components/integrations/saml/saml-config-form.tsx +++ b/ui/components/integrations/saml/saml-config-form.tsx @@ -14,9 +14,9 @@ import { createSamlConfig, updateSamlConfig } from "@/actions/integrations"; import { AddIcon } from "@/components/icons"; import { Button, Card, CardContent, CardHeader } from "@/components/shadcn"; import { useToast } from "@/components/ui"; +import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet"; import { CustomServerInput } from "@/components/ui/custom"; import { CustomLink } from "@/components/ui/custom/custom-link"; -import { SnippetChip } from "@/components/ui/entities"; import { FormButtons } from "@/components/ui/form"; import { apiBaseUrl } from "@/lib"; @@ -258,7 +258,11 @@ export const SamlConfigForm = ({ : `${apiBaseUrl}/accounts/saml/your-domain.com/acs/`; return ( -
+
Need help configuring SAML SSO?{" "} ACS URL: -
@@ -315,9 +319,9 @@ export const SamlConfigForm = ({ Audience: - diff --git a/ui/components/invitations/invitation-details.tsx b/ui/components/invitations/invitation-details.tsx index 38d831bbfa..0df447907f 100644 --- a/ui/components/invitations/invitation-details.tsx +++ b/ui/components/invitations/invitation-details.tsx @@ -1,11 +1,11 @@ "use client"; -import { Snippet } from "@heroui/snippet"; import Link from "next/link"; import { AddIcon } from "../icons"; import { Button, Card, CardContent, CardHeader } from "../shadcn"; import { Separator } from "../shadcn/separator/separator"; +import { CodeSnippet } from "../ui/code-snippet/code-snippet"; import { DateWithTime } from "../ui/entities"; interface InvitationDetailsProps { @@ -89,20 +89,7 @@ export const InvitationDetails = ({ attributes }: InvitationDetailsProps) => { Share this link with the user: -
- -

{invitationLink}

-
-
+
diff --git a/ui/components/scans/table/scan-detail.tsx b/ui/components/scans/table/scan-detail.tsx index 0cedd7e9c0..6c650271a3 100644 --- a/ui/components/scans/table/scan-detail.tsx +++ b/ui/components/scans/table/scan-detail.tsx @@ -1,8 +1,7 @@ "use client"; -import { Snippet } from "@heroui/snippet"; - import { Card, CardContent, CardHeader, CardTitle } from "@/components/shadcn"; +import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet"; import { DateWithTime, EntityInfo, InfoField } from "@/components/ui/entities"; import { StatusBadge } from "@/components/ui/table/status-badge"; import { ProviderProps, ProviderType, ScanProps, TaskDetails } from "@/types"; @@ -81,18 +80,17 @@ export const ScanDetail = ({
- {scanDetails.id} + {scan.state === "failed" && taskDetails?.attributes.result && ( <> {taskDetails.attributes.result.exc_message && ( - - - {taskDetails.attributes.result.exc_message.join("\n")} - - + )}
diff --git a/ui/components/ui/code-snippet/code-snippet.tsx b/ui/components/ui/code-snippet/code-snippet.tsx index 7fefa8b552..9440b500e6 100644 --- a/ui/components/ui/code-snippet/code-snippet.tsx +++ b/ui/components/ui/code-snippet/code-snippet.tsx @@ -21,6 +21,10 @@ interface CodeSnippetProps { icon?: ReactNode; /** Function to format the displayed text (value is still copied as-is) */ formatter?: (value: string) => string; + /** Enable multiline display (disables truncation, enables word wrap) */ + multiline?: boolean; + /** Custom aria-label for the copy button */ + ariaLabel?: string; } export const CodeSnippet = ({ @@ -30,6 +34,8 @@ export const CodeSnippet = ({ hideCopyButton = false, icon, formatter, + multiline = false, + ariaLabel = "Copy to clipboard", }: CodeSnippetProps) => { const [copied, setCopied] = useState(false); @@ -46,7 +52,7 @@ export const CodeSnippet = ({ type="button" onClick={handleCopy} className="text-text-neutral-secondary hover:text-text-neutral-primary shrink-0 cursor-pointer transition-colors" - aria-label="Copy to clipboard" + aria-label={ariaLabel} > {copied ? ( @@ -66,7 +72,7 @@ export const CodeSnippet = ({ "hover:bg-bg-neutral-tertiary text-text-neutral-secondary hover:text-text-neutral-primary shrink-0 cursor-pointer rounded-md p-1 transition-colors", className, )} - aria-label="Copy to clipboard" + aria-label={ariaLabel} > {copied ? ( @@ -80,19 +86,26 @@ export const CodeSnippet = ({ return (
{icon && ( {icon} )} - - - {displayValue} - - {value} - + {multiline ? ( + + {displayValue} + + ) : ( + + + {displayValue} + + {value} + + )} {!hideCopyButton && }
); diff --git a/ui/components/ui/entities/index.ts b/ui/components/ui/entities/index.ts index 88cf9504f3..d2b2b9aa22 100644 --- a/ui/components/ui/entities/index.ts +++ b/ui/components/ui/entities/index.ts @@ -3,4 +3,3 @@ export * from "./entity-info"; export * from "./get-provider-logo"; export * from "./info-field"; export * from "./scan-status"; -export * from "./snippet-chip"; diff --git a/ui/components/ui/entities/snippet-chip.tsx b/ui/components/ui/entities/snippet-chip.tsx deleted file mode 100644 index 04dabfde67..0000000000 --- a/ui/components/ui/entities/snippet-chip.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Snippet } from "@heroui/snippet"; -import { cn } from "@heroui/theme"; -import { Tooltip } from "@heroui/tooltip"; -import React from "react"; - -import { CopyIcon, DoneIcon } from "@/components/icons"; - -interface SnippetChipProps { - value: string; - ariaLabel?: string; - icon?: React.ReactNode; - hideCopyButton?: boolean; - formatter?: (value: string) => string; - className?: string; -} -export const SnippetChip = ({ - value, - hideCopyButton = false, - ariaLabel = `Copy ${value} to clipboard`, - icon, - formatter, - className, - ...props -}: SnippetChipProps) => { - return ( - } - checkIcon={} - hideCopyButton={hideCopyButton} - codeString={value} - {...props} - > -
- {icon} - - - {formatter ? formatter(value) : value} - - -
-
- ); -}; diff --git a/ui/components/ui/table/data-table-pagination.tsx b/ui/components/ui/table/data-table-pagination.tsx index 6ba2d9a158..7f3f5eb83d 100644 --- a/ui/components/ui/table/data-table-pagination.tsx +++ b/ui/components/ui/table/data-table-pagination.tsx @@ -191,7 +191,7 @@ export function DataTablePagination({ {/* Page info and navigation */}
- + Page {currentPage} of {totalPages}
diff --git a/ui/components/users/profile/api-key-success-modal.tsx b/ui/components/users/profile/api-key-success-modal.tsx index c3e039cab8..ee170b3738 100644 --- a/ui/components/users/profile/api-key-success-modal.tsx +++ b/ui/components/users/profile/api-key-success-modal.tsx @@ -1,10 +1,9 @@ "use client"; -import { Snippet } from "@heroui/snippet"; - import { Button } from "@/components/shadcn"; import { Modal } from "@/components/shadcn/modal"; import { Alert, AlertDescription } from "@/components/ui/alert/Alert"; +import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet"; interface ApiKeySuccessModalProps { isOpen: boolean; @@ -36,18 +35,12 @@ export const ApiKeySuccessModal = ({

Your API Key

- - {apiKey} - +
diff --git a/ui/components/users/profile/membership-item.tsx b/ui/components/users/profile/membership-item.tsx index b13574ad8e..23c616600c 100644 --- a/ui/components/users/profile/membership-item.tsx +++ b/ui/components/users/profile/membership-item.tsx @@ -32,8 +32,8 @@ export const MembershipItem = ({ setIsOpen={setIsEditOpen} /> - -
+ +
{membership.attributes.role} diff --git a/ui/components/users/profile/revoke-api-key-modal.tsx b/ui/components/users/profile/revoke-api-key-modal.tsx index 781440bc92..6097b747c5 100644 --- a/ui/components/users/profile/revoke-api-key-modal.tsx +++ b/ui/components/users/profile/revoke-api-key-modal.tsx @@ -1,6 +1,5 @@ "use client"; -import { Snippet } from "@heroui/snippet"; import { Trash2Icon } from "lucide-react"; import { revokeApiKey } from "@/actions/api-keys/api-keys"; @@ -10,6 +9,7 @@ import { AlertDescription, AlertTitle, } from "@/components/ui/alert/Alert"; +import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet"; import { ModalButtons } from "@/components/ui/custom/custom-modal-buttons"; import { FALLBACK_VALUES } from "./api-keys/constants"; @@ -67,16 +67,12 @@ export const RevokeApiKeyModal = ({

Are you sure you want to revoke this API key?

- -

{apiKey?.attributes.name || FALLBACK_VALUES.UNNAMED_KEY}

-

Prefix: {apiKey?.attributes.prefix}

-
+
{error && ( diff --git a/ui/components/users/profile/role-item.tsx b/ui/components/users/profile/role-item.tsx index 16264c58a7..5e1f6feef4 100644 --- a/ui/components/users/profile/role-item.tsx +++ b/ui/components/users/profile/role-item.tsx @@ -68,6 +68,7 @@ export const RoleItem = ({ variant="ghost" size="sm" onClick={() => setIsExpanded(!isExpanded)} + className="px-0" > {isExpanded ? "Hide details" : "Show details"} diff --git a/ui/components/users/profile/user-basic-info-card.tsx b/ui/components/users/profile/user-basic-info-card.tsx index fa3376ad84..4c85185e2d 100644 --- a/ui/components/users/profile/user-basic-info-card.tsx +++ b/ui/components/users/profile/user-basic-info-card.tsx @@ -4,13 +4,14 @@ import { Divider } from "@heroui/divider"; import { ProwlerShort } from "@/components/icons"; import { Card, CardContent } from "@/components/shadcn"; -import { DateWithTime, InfoField, SnippetChip } from "@/components/ui/entities"; +import { CodeSnippet } from "@/components/ui/code-snippet/code-snippet"; +import { DateWithTime, InfoField } from "@/components/ui/entities"; import { UserDataWithRoles } from "@/types/users"; const TenantIdCopy = ({ id }: { id: string }) => { return (
- +
); };