From f8d1b5691a5cc4e57b350b0121e08542cace2d47 Mon Sep 17 00:00:00 2001 From: Erica Hinkle Date: Thu, 30 Jul 2026 13:42:11 -0400 Subject: [PATCH] OCPBUGS-95592: Humanize memory and storage values in ResourceQuota display This change improves the readability of ResourceQuota displays by formatting memory and storage-related values as human-readable binary units (e.g., GiB, MiB) instead of raw byte values. Key changes: - Add humanizeBinaryBytes import from utils/units - Add isBinaryResourceType() helper to identify memory/storage resources - Add formatResourceValue() to conditionally format values - Update ResourceUsageRow to use formatResourceValue for all displayed values Resources affected: - memory (e.g., limits.memory, requests.memory) - storage (e.g., persistentvolumeclaims, requests.storage) - ephemeral-storage Jira: https://issues.redhat.com/browse/OCPBUGS-95592 --- .../__tests__/resource-quota.spec.tsx | 58 +++++++++++++++++++ frontend/public/components/resource-quota.jsx | 33 +++++++++-- 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/frontend/public/components/__tests__/resource-quota.spec.tsx b/frontend/public/components/__tests__/resource-quota.spec.tsx index cec20323dbf..d64479df762 100644 --- a/frontend/public/components/__tests__/resource-quota.spec.tsx +++ b/frontend/public/components/__tests__/resource-quota.spec.tsx @@ -188,3 +188,61 @@ describe('Check applied cluster quota table columns by ResourceUsageRow', () => expect(screen.getByText('2')).toBeVisible(); }); }); + +describe('Check memory resource humanization in ResourceUsageRow', () => { + const memoryQuota = { + apiVersion: 'v1', + kind: 'ResourceQuota', + metadata: { name: 'example', namespace: 'example' }, + spec: { hard: { 'requests.memory': '2147483648' } }, + status: { + hard: { 'requests.memory': '2147483648' }, + used: { 'requests.memory': '1073741824' }, + }, + }; + + it('displays memory values in human-readable format (GiB)', () => { + renderWithProviders( + + + + +
, + ); + + // Verify the resource type + expect(screen.getByText('requests.memory')).toBeVisible(); + + // Verify memory values are humanized (1 GiB used, 2 GiB limit) + expect(screen.getByText('1 GiB')).toBeVisible(); + expect(screen.getByText('2 GiB')).toBeVisible(); + }); + + const storageQuota = { + apiVersion: 'v1', + kind: 'ResourceQuota', + metadata: { name: 'example', namespace: 'example' }, + spec: { hard: { 'requests.storage': '10737418240' } }, + status: { + hard: { 'requests.storage': '10737418240' }, + used: { 'requests.storage': '5368709120' }, + }, + }; + + it('displays storage values in human-readable format (GiB)', () => { + renderWithProviders( + + + + +
, + ); + + // Verify the resource type + expect(screen.getByText('requests.storage')).toBeVisible(); + + // Verify storage values are humanized (5 GiB used, 10 GiB limit) + expect(screen.getByText('5 GiB')).toBeVisible(); + expect(screen.getByText('10 GiB')).toBeVisible(); + }); +}); diff --git a/frontend/public/components/resource-quota.jsx b/frontend/public/components/resource-quota.jsx index 744cea18954..40470e41287 100644 --- a/frontend/public/components/resource-quota.jsx +++ b/frontend/public/components/resource-quota.jsx @@ -56,7 +56,7 @@ import { useAccessReview } from './utils/rbac'; import { ResourceLink } from './utils/resource-link'; import { Selector } from './utils/selector'; import { LoadingBox } from './utils/status-box'; -import { convertToBaseValue } from './utils/units'; +import { convertToBaseValue, humanizeBinaryBytes } from './utils/units'; const isClusterQuota = (quota) => !quota.metadata.namespace; @@ -81,6 +81,27 @@ const getQuotaResourceTypes = (quota) => { return _.keys(specHard).sort(); }; +const isBinaryResourceType = (resourceType) => { + // Resource types that should be displayed as binary bytes (e.g., GiB, MiB) + return ( + resourceType.includes('memory') || + resourceType.includes('storage') || + resourceType.includes('ephemeral-storage') + ); +}; + +const formatResourceValue = (value, resourceType) => { + if (value === null || value === undefined) { + return DASH; + } + if (isBinaryResourceType(resourceType)) { + // Convert string to number for humanizeBinaryBytes + const numericValue = typeof value === 'string' ? parseFloat(value) : value; + return humanizeBinaryBytes(numericValue).string; + } + return value; +}; + export const getACRQResourceUsage = (quota, resourceType, namespace) => { let used; if (namespace) { @@ -189,9 +210,9 @@ export const ResourceUsageRow = ({ quota, resourceType, namespace = undefined }) - {used.namespace} - {totalUsed} - {max} + {formatResourceValue(used.namespace, resourceType)} + {formatResourceValue(totalUsed, resourceType)} + {formatResourceValue(max, resourceType)} ); } @@ -203,8 +224,8 @@ export const ResourceUsageRow = ({ quota, resourceType, namespace = undefined }) - {used} - {max} + {formatResourceValue(used, resourceType)} + {formatResourceValue(max, resourceType)} ); };