From 43ddad6868dc6697000f7abf1e28e66b1c03c445 Mon Sep 17 00:00:00 2001 From: Oksana Bazylieva Date: Mon, 20 Jul 2026 14:36:18 -0400 Subject: [PATCH 1/7] Display Cluster Description with Markdown Rendering Signed-off-by: Oksana Bazylieva --- .../ClusterOverview/ClusterOverview.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx b/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx index f1466fa450..c4af0c6570 100644 --- a/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx +++ b/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx @@ -341,12 +341,15 @@ export function ClusterOverviewPageContent() { }, description: { key: t('Description'), - value: cluster?.annotations?.[clusterDescriptionAnnotation] ? ( + value: ( - + - ) : ( - '-' ), }, } From d5590736eb17be0fb7d2b4da286e04ee5e219b59 Mon Sep 17 00:00:00 2001 From: Oksana Bazylieva Date: Mon, 20 Jul 2026 14:48:25 -0400 Subject: [PATCH 2/7] Remove mock description Signed-off-by: Oksana Bazylieva --- .../ClusterOverview/ClusterOverview.tsx | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx b/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx index c4af0c6570..f1466fa450 100644 --- a/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx +++ b/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx @@ -341,15 +341,12 @@ export function ClusterOverviewPageContent() { }, description: { key: t('Description'), - value: ( + value: cluster?.annotations?.[clusterDescriptionAnnotation] ? ( - + + ) : ( + '-' ), }, } From d29f008be0023e0dc79cc905a107e88136a4276b Mon Sep 17 00:00:00 2001 From: Oksana Bazylieva Date: Mon, 20 Jul 2026 22:53:38 -0400 Subject: [PATCH 3/7] Implement cluster description edit modal with markdown formatting Signed-off-by: Oksana Bazylieva --- .../ClusterOverview/ClusterOverview.tsx | 30 +++- .../components/EditDescription.tsx | 170 ++++++++++++++++++ 2 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 frontend/src/routes/Infrastructure/Clusters/ManagedClusters/components/EditDescription.tsx diff --git a/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx b/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx index f1466fa450..3dc13143f3 100644 --- a/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx +++ b/frontend/src/routes/Infrastructure/Clusters/ManagedClusters/ClusterDetails/ClusterOverview/ClusterOverview.tsx @@ -5,7 +5,7 @@ import { ClusterDeploymentK8sResource, getClusterProperties, } from '@openshift-assisted/ui-lib/cim' -import { AlertVariant, ButtonVariant, Content, PageSection, Popover } from '@patternfly/react-core' +import { AlertVariant, ButtonVariant, PageSection, Popover } from '@patternfly/react-core' import { Modal, ModalVariant } from '@patternfly/react-core/deprecated' import { ExternalLinkAltIcon, OutlinedQuestionCircleIcon, PencilAltIcon } from '@patternfly/react-icons' import { Markdown } from '@redhat-cloud-services/rule-components/Markdown' @@ -47,6 +47,7 @@ import AIClusterDetails from '../../components/cim/AIClusterDetails' import AIHypershiftClusterDetails from '../../components/cim/AIHypershiftClusterDetails' import { ClusterStatusMessageAlert } from '../../components/ClusterStatusMessageAlert' import { DistributionField } from '../../components/DistributionField' +import { EditDescription } from '../../components/EditDescription' import { EditLabels } from '../../components/EditLabels' import { HiveNotification } from '../../components/HiveNotification' import HypershiftClusterDetails from '../../components/HypershiftClusterDetails' @@ -85,6 +86,7 @@ export function ClusterOverviewPageContent() { const localHubName = useLocalHubName() const clusterDescriptionAnnotation = 'console.open-cluster-management.io/description' const [showEditLabels, setShowEditLabels] = useState(false) + const [showEditDescription, setShowEditDescription] = useState(false) const [showChannelSelectModal, setShowChannelSelectModal] = useState(false) const [curatorSummaryModalIsOpen, setCuratorSummaryModalIsOpen] = useState(false) const { projects } = useProjects() @@ -342,12 +344,20 @@ export function ClusterOverviewPageContent() { description: { key: t('Description'), value: cluster?.annotations?.[clusterDescriptionAnnotation] ? ( - - - + ) : ( '-' ), + keyAction: cluster?.isManaged && ( + setShowEditDescription(true)} + variant={ButtonVariant.plain} + aria-label={t('Edit cluster description')} + rbac={[rbacPatch(ManagedClusterDefinition, undefined, cluster?.name)]} + > + + + ), }, } @@ -506,6 +516,18 @@ export function ClusterOverviewPageContent() { displayName={cluster.displayName} close={() => setShowEditLabels(false)} /> + setShowEditDescription(false)} + /> {details} void }) { + const { t } = useTranslation() + const [description, setDescription] = useState('') + const textAreaRef = useRef(null) + const isOpen = props.resource !== undefined + + useLayoutEffect(() => { + if (isOpen) { + const desc = props.resource?.metadata?.annotations?.[CLUSTER_DESCRIPTION_ANNOTATION] ?? '' + setDescription(desc) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isOpen]) + + const insertMarkdown = (prefix: string, suffix: string = prefix) => { + const textarea = textAreaRef.current + if (!textarea) return + + const start = textarea.selectionStart + const end = textarea.selectionEnd + const selectedText = description.substring(start, end) + const beforeText = description.substring(0, start) + const afterText = description.substring(end) + + const newText = beforeText + prefix + selectedText + suffix + afterText + setDescription(newText) + + // Place cursor between opening and closing markdown markers (e.g., **|** for typing) + setTimeout(() => { + const scrollPosition = textarea.scrollTop + textarea.focus() + const newPosition = start + prefix.length + selectedText.length + textarea.setSelectionRange(newPosition, newPosition) + textarea.scrollTop = scrollPosition + }, 0) + } + + return ( + + + {(alertContext) => ( + + + + + + + + + + + + + + + + + + + + + +