Skip to content

Conversation

@vojtechszocs
Copy link
Contributor

@vojtechszocs vojtechszocs commented Nov 19, 2025

Depends on #15713

  • packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • public/components/persistent-volume-claim.tsx
  • public/components/persistent-volume.tsx
  • public/components/storage-class.tsx
  • public/components/volumes-table.tsx

@vojtechszocs vojtechszocs changed the title Migrate storage related tables to ConsoleDataView CONSOLE-4723: Migrate storage related tables to ConsoleDataView Nov 19, 2025
@openshift-ci openshift-ci bot requested review from TheRealJon and jhadvig November 19, 2025 21:28
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Nov 19, 2025

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: vojtechszocs

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 19, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

Made ConsoleDataView's initialFilters optional and exported initialFiltersDefault; defaulted GetDataViewRows second generic to any; removed many explicit initialFilters usages (some replaced by memoized local initialFilters); migrated many volume-snapshot and PVC/PV lists/pages from table-based rendering to ConsoleDataView with new row/column hooks.

Changes

Cohort / File(s) Summary
Core Data‑View
frontend/packages/console-app/src/components/data-view/ConsoleDataView.tsx, frontend/packages/console-app/src/components/data-view/types.ts
Exported initialFiltersDefault: ResourceFilters; made ConsoleDataViewProps.initialFilters optional and defaulted it in parameter destructuring; added TCustomRowData = any default to GetDataViewRows.
Removed/omitted initialFilters prop usages
Many files under frontend/packages/helm-plugin/... and frontend/public/components/... (examples: frontend/packages/console-app/src/components/pdb/PDBList.tsx, frontend/packages/helm-plugin/src/components/..., frontend/public/components/alert-manager.tsx, build*.tsx, configmap.tsx, cron-job.tsx, deployment*.tsx, image-stream.tsx, job.tsx, machine-*.tsx, replicaset.jsx, replication-controller.jsx, resource-quota.jsx, service-account.jsx, service-monitor.jsx, stateful-set.tsx, user.tsx, etc.)
Removed import of initialFiltersDefault and removed initialFilters={initialFiltersDefault} prop usage; components now either rely on ConsoleDataView defaults or supply their own memoized initial filters.
Memoized local initialFilters
frontend/packages/console-app/src/components/nodes/NodesPage.tsx, frontend/packages/helm-plugin/src/components/list-page/HelmReleaseList.tsx, frontend/public/components/RBAC/bindings.tsx, frontend/public/components/RBAC/role.jsx, frontend/public/components/cluster-settings/cluster-operator.tsx, frontend/public/components/namespace.jsx, frontend/public/components/pod-list.tsx, frontend/public/components/template-instance.tsx
Introduced useMemo-created stable initialFilters objects (merging initialFiltersDefault with overrides like empty status, roles, requester arrays) and passed them to ConsoleDataView.
GetDataViewRows generic / signature simplification
Many row/row-test files under frontend/packages/helm-plugin/..., frontend/public/components/... (examples: *List.tsx, *Row.tsx, *spec.tsx)
Removed the second generic parameter (previously often , undefined) from GetDataViewRows<T, ...> usages to GetDataViewRows<T>; simplified function signatures by removing explicit RowProps / ConsoleDataViewColumn / ConsoleDataViewRow parameter/return typings.
Volume‑snapshot, PVC, PV, StorageClass migrations
frontend/packages/console-app/src/components/volume-snapshot/*, frontend/public/components/persistent-volume-claim.tsx, frontend/public/components/persistent-volume.tsx, frontend/public/components/storage-class.tsx
Replaced legacy table/VirtualizedTable implementations with ConsoleDataView-driven flows: added getDataViewRows, column hooks (use*Columns), LoadingBox/Suspense fallbacks, removed unfilteredData props, added new filter/row types, and converted several components to React.FCC signatures.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas to pay extra attention:

  • ConsoleDataView changes: verify initialFiltersDefault export, typing, and the defaulting in props to avoid runtime/typing regressions.
  • Volume-snapshot / PVC / PV / StorageClass migrations: confirm public prop/signature changes (removed unfilteredData, new FCC signatures), column hooks, and Suspense/LoadingBox usage.
  • Sites where initialFilters was removed vs where memoized initialFilters was introduced: ensure consistent filter semantics and stable object identity for hooks that depend on it.
  • Widespread GetDataViewRows signature simplifications: ensure no callers or tests still expect the removed second generic or explicit RowProps/column typing.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Nov 19, 2025
@openshift-ci openshift-ci bot added the component/core Related to console core functionality label Nov 19, 2025
@vojtechszocs
Copy link
Contributor Author

/hold

@openshift-ci openshift-ci bot added component/helm Related to helm-plugin approved Indicates a PR has been approved by an approver from all required OWNERS files. component/monitoring Related to monitoring do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. labels Nov 19, 2025
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 19, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on #15713

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 19, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on #15713

  • packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • public/components/persistent-volume-claim.tsx
  • public/components/persistent-volume.jsx
  • public/components/storage-class.tsx
  • public/components/volumes-table.tsx

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (1)

51-53: Consider normalizing restoreSize before humanizing for consistency

Here you call humanizeBinaryBytes(size).string directly on obj.status?.restoreSize. In volume-snapshot.tsx, size is first normalized with convertToBaseValue before humanization. Aligning both places (e.g., via a shared helper or the same pre-normalization step) would avoid subtle unit-handling differences and keep the UX consistent between snapshots and snapshot contents.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (2)

63-80: Make getDataViewRows resilient to missing rowData

Right now you destructure rowData: { hideSnapshotContentColumn } directly from each element. This assumes rowData is always present, which is true given the current customRowData usage, but it makes the helper fragile to future reuse. A small tweak to handle an optional rowData (e.g., const hideSnapshotContentColumn = rowData?.hideSnapshotContentColumn;) would make this safer without changing behavior today.


227-275: Confirm status filter values stay aligned with volumeSnapshotStatus

The status filter options (Ready, Pending, Error) are hard-coded, and matchesAdditionalFilters checks volumeSnapshotStatus(resource) against these strings. This is fine as long as volumeSnapshotStatus continues to return the same labels, but it’s a silent coupling. Worth double‑checking they match today and considering a shared source of truth if more statuses are added later.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between b34dc56 and 8f69bf3.

📒 Files selected for processing (4)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (4 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
🔇 Additional comments (4)
frontend/packages/console-app/src/components/nodes/NodesPage.tsx (1)

548-551: LGTM! Good use of memoization for stable filter reference.

The memoized initialFilters prevents unnecessary object recreation on every render, which could cause filter resets in ConsoleDataView. The empty dependency array is appropriate since this filter configuration is truly static.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (1)

45-103: ConsoleDataView row mapping looks consistent and robust

The new getDataViewRows implementation builds row cells from VolumeSnapshotContentKind cleanly, uses DASH fallbacks, and wires actions/name/status/links in a data-driven way that should play nicely with ConsoleDataView and column management. No correctness issues stand out here.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (1)

41-139: Data-view migration for VolumeSnapshotClass looks solid

The combination of getDataViewRows, useVolumeSnapshotClassColumns, and the ConsoleDataView wrapper preserves existing behavior (default-class badge, driver/deletionPolicy display, actions) while standardizing on the new data-view patterns. Row construction, column IDs, and DASH fallbacks are consistent; I don’t see functional or maintainability concerns here.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (1)

63-146: Snapshot row construction and conditional snapshotContent column look correct

getDataViewRows correctly derives name/namespace/status/size/source/content/class/timestamp and wires actions/context. The hideSnapshotContentColumn flag is applied consistently in both the column definitions and the per-row disabled field, so the snapshot content column should cleanly disappear when CAN_LIST_VSC is false without leaving empty cells.

@vojtechszocs vojtechszocs force-pushed the data-view-storage-tables branch from 8f69bf3 to 96cafe4 Compare November 20, 2025 19:54
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
frontend/public/components/persistent-volume-claim.tsx (2)

481-545: Forward canCreate and showTitle props to ListPage component

The review comment is accurate. PersistentVolumeClaimsPageProps (lines 590-594) defines canCreate? and showTitle? as optional properties, but the component destructures only namespace and spreads remaining props without explicitly handling these two. The component then hardcodes canCreate={true} and never forwards showTitle, breaking the type contract and preventing callers from controlling these behaviors.

The suggested fix is correct: extract both props with appropriate defaults and forward them to ListPage instead of hardcoding canCreate={true} and omitting showTitle.


489-508: Dispatch in render phase confirmed—infinite render loop risk is real and critical

Verification confirms the review comment is accurate. The code has:

  1. dispatch(setPVCMetrics(pvcMetrics)) directly in component body (line 507), not wrapped in any effect
  2. No useEffect anywhere in the component that might mitigate this
  3. pvcMetrics is rebuilt each render via the ternary + reduce (no memoization)
  4. dispatch is a fresh function from useDispatch() each render

This creates a render loop: pvcMetrics object → dispatch → store update → re-render → new pvcMetrics object → repeat.

The suggested fix is correct—use useMemo for pvcMetrics and useEffect for the dispatch call with proper dependency arrays.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (1)

335-369: Ensure checkPVCSnapshot always returns an array to the table

checkPVCSnapshot currently uses optional chaining:

const checkPVCSnapshot = (
  volumeSnapshots: VolumeSnapshotKind[],
  pvc: K8sResourceKind,
): VolumeSnapshotKind[] =>
  volumeSnapshots?.filter(/* ... */);

If volumeSnapshots is ever null/undefined, this expression returns undefined, but VolumeSnapshotTable expects data: VolumeSnapshotKind[]. To avoid passing undefined into the table, return an empty array as a fallback:

 const checkPVCSnapshot = (
   volumeSnapshots: VolumeSnapshotKind[],
   pvc: K8sResourceKind,
 ): VolumeSnapshotKind[] =>
-  volumeSnapshots?.filter(
+  volumeSnapshots?.filter(
     (snapshot) =>
       snapshot?.spec?.source?.persistentVolumeClaimName === getName(pvc) &&
       getNamespace(snapshot) === getNamespace(pvc),
-  );
+  ) ?? [];

This keeps the runtime type consistent with the VolumeSnapshotTable contract.

🧹 Nitpick comments (4)
frontend/public/components/persistent-volume-claim.tsx (2)

103-174: Avoid using hooks directly inside getDataViewRows utility

getDataViewRows calls useTranslation and useSelector while explicitly disabling react-hooks/rules-of-hooks. This is brittle: if ConsoleDataView ever calls this callback conditionally or in a non-render context, hook ordering can break, and static analysis can’t protect you.

Consider moving the hook usage into a dedicated hook that returns a memoized row-mapping function, e.g. usePVCDataViewRows(): GetDataViewRows<PersistentVolumeClaimKind>, and then pass that from PersistentVolumeClaimList to ConsoleDataView. That keeps hooks at the top level of a React hook/component and lets you re‑enable the eslint rule.


584-588: Align PersistentVolumeClaimListProps['data'] type with what GetDataViewRows expects

PersistentVolumeClaimListProps currently types data as PersistentVolumeClaimKind[], but getDataViewRows treats each element as an object with an obj property. To avoid type drift and ensure callers pass the correct row shape, it’s safer to derive the element type from GetDataViewRows:

-type PersistentVolumeClaimListProps = {
-  data: PersistentVolumeClaimKind[];
+type PersistentVolumeClaimListProps = {
+  data: Parameters<GetDataViewRows<PersistentVolumeClaimKind>>[0];
   loaded: boolean;
   loadError: unknown;
 };

This keeps the list’s public props in sync with the DataView row contract even if the underlying row type changes.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (1)

45-53: Consider normalizing restoreSize before humanizing

In getDataViewRows, sizeMetrics calls humanizeBinaryBytes(size) directly, whereas the VolumeSnapshot table converts quantities via convertToBaseValue first. If status.restoreSize is a quantity string, mirroring that pattern here (humanizeBinaryBytes(convertToBaseValue(size))) would keep units handling consistent and more robust.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (1)

34-39: Use computed property name for the default-class annotation key

In isDefaultSnapshotClass, the default annotations object uses a literal key defaultSnapshotClassAnnotation:

getAnnotations(volumeSnapshotClass, { defaultSnapshotClassAnnotation: 'false' })[defaultSnapshotClassAnnotation]

Using a computed key keeps the constant and the actual annotation key in sync and makes the fallback clearer:

-  getAnnotations(volumeSnapshotClass, { defaultSnapshotClassAnnotation: 'false' })[
-    defaultSnapshotClassAnnotation
-  ] === 'true';
+  getAnnotations(volumeSnapshotClass, { [defaultSnapshotClassAnnotation]: 'false' })[
+    defaultSnapshotClassAnnotation
+  ] === 'true';

This avoids any future drift if the annotation name changes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 8f69bf3 and 96cafe4.

📒 Files selected for processing (5)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (4 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (4 hunks)
  • frontend/public/components/persistent-volume-claim.tsx (12 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • frontend/public/components/persistent-volume-claim.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
🔇 Additional comments (7)
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (3)

54-103: Row mapping to ConsoleDataView looks consistent

rowCells keys are aligned with tableColumnInfo and the columns IDs, and the fallback to DASH plus optional props handling makes the mapping resilient to missing fields. No functional issues spotted in the row construction.


105-176: Columns hook and ConsoleDataView integration are wired correctly

useVolumeSnapshotContentColumns defines stable, memoized columns with appropriate sort keys and sticky/nowrap props, and VolumeSnapshotContentTable passes data, loaded, loadError, columns, and getDataViewRows into ConsoleDataView inside a Suspense with LoadingBox fallback. This aligns with the new data-view patterns and looks good.


180-206: Page-level watch and table composition look good

VolumeSnapshotContentPage watches the correct GVK, treats the resource as non‑namespaced, passes loadError through to the table, and uses ListPageCreate with the shared kind reference. The title/showTitle/canCreate wiring is straightforward and appears correct.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (2)

41-139: ConsoleDataView migration for VolumeSnapshotClass looks solid

getDataViewRows correctly builds cells for name/driver/deletionPolicy/actions, including the “Default” marker and action menu context, and useVolumeSnapshotClassColumns defines matching columns with sticky/nowrap props. VolumeSnapshotClassTable then passes these into ConsoleDataView with loaded, loadError, and a LoadingBox fallback. Overall the new data-view implementation is coherent and free of obvious issues.


143-174: Page wiring and creation flow remain intact

VolumeSnapshotClassPage still watches the correct model, threads namespace and selector, and now uses the shared kind reference for ListPageCreate while passing loadError to the table. The behavior should match the old page while benefiting from the new table implementation.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (2)

63-146: Row mapping and conditional SnapshotContent cell disabling look correct

getDataViewRows builds cells for all columns, uses VolumeSnapshotRowData.hideSnapshotContentColumn to mark the SnapshotContent cell as disabled, and correctly falls back to DASH for missing values. The filter over columns and the use of referenceForModel / referenceFor for links and action context all look consistent with the intended behavior.


148-295: DataView columns, status filters, and flag-based column visibility are well-structured

useVolumeSnapshotColumns cleanly encapsulates column definitions and hides the SnapshotContent column when hideSnapshotContentColumn is true, while VolumeSnapshotTable wires in customRowData, a status checkbox filter, and matchesAdditionalFilters to feed ConsoleDataView. The use of initialFiltersDefault and memoized filter options keeps configuration centralized and avoids unnecessary re-renders. No blocking issues here.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 20, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on #15713

  • packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • public/components/persistent-volume-claim.tsx
  • public/components/persistent-volume.jsx
  • public/components/storage-class.tsx
  • public/components/volumes-table.tsx

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 20, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on #15713

  • packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • public/components/persistent-volume-claim.tsx
  • public/components/persistent-volume.tsx
  • public/components/storage-class.tsx
  • public/components/volumes-table.tsx

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@vojtechszocs vojtechszocs force-pushed the data-view-storage-tables branch from 96cafe4 to ab0aeb5 Compare November 20, 2025 20:46
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 20, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on #15713

  • packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • public/components/persistent-volume-claim.tsx
  • public/components/persistent-volume.tsx
  • public/components/storage-class.tsx
  • public/components/volumes-table.tsx

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
frontend/public/components/persistent-volume-claim.tsx (2)

236-247: Fix useFlag usage so the PV link can actually be shown

useFlag(FLAGS.CAN_LIST_PV) is treated as if it returned a flags map:

const flags = useFlag(FLAGS.CAN_LIST_PV);
const canListPV = flags[FLAGS.CAN_LIST_PV];

If useFlag returns a single boolean (as elsewhere in Console), canListPV will always be undefined, and the volumeName && canListPV && ... block will never render.

Refactor to use the boolean directly:

-const PVCDetails: React.FCC<PVCDetailsProps> = ({ obj: pvc }) => {
-  const flags = useFlag(FLAGS.CAN_LIST_PV);
-  const { t } = useTranslation();
-  ...
-  const canListPV = flags[FLAGS.CAN_LIST_PV];
+const PVCDetails: React.FCC<PVCDetailsProps> = ({ obj: pvc }) => {
+  const canListPV = useFlag(FLAGS.CAN_LIST_PV);
+  const { t } = useTranslation();
+  ...
   {volumeName && canListPV && (
     <DescriptionListGroup>
       <DescriptionListTerm>{t('public~PersistentVolumes')}</DescriptionListTerm>
       <DescriptionListDescription data-test-id="persistent-volume">
         <ResourceLink kind="PersistentVolume" name={volumeName} />
       </DescriptionListDescription>
     </DescriptionListGroup>
   )}

Also applies to: 385-391


482-509: Avoid dispatching setPVCMetrics directly in render; memoize + useEffect

PersistentVolumeClaimsPage currently computes metrics and dispatches on every render:

const [response, loadError, loading] = usePrometheusPoll(...);

const pvcMetrics =
  _.isEmpty(loadError) && !loading
    ? response?.data?.result?.reduce(/* ... */)
    : {};

dispatch(setPVCMetrics(pvcMetrics));

Dispatching during render is brittle and can lead to unnecessary renders or even render loops if reducers touch state consumed here.

Consider:

-  const pvcMetrics =
-    _.isEmpty(loadError) && !loading
-      ? response?.data?.result?.reduce((acc, item) => {
-          _.set(
-            acc,
-            ['usedCapacity', item?.metric?.namespace, item?.metric?.persistentvolumeclaim],
-            Number(item?.value?.[1]),
-          );
-          return acc;
-        }, {})
-      : {};
-
-  dispatch(setPVCMetrics(pvcMetrics));
+  const pvcMetrics = React.useMemo(
+    () =>
+      _.isEmpty(loadError) && !loading
+        ? response?.data?.result?.reduce((acc, item) => {
+            _.set(
+              acc,
+              ['usedCapacity', item?.metric?.namespace, item?.metric?.persistentvolumeclaim],
+              Number(item?.value?.[1]),
+            );
+            return acc;
+          }, {})
+        : {},
+    [response, loadError, loading],
+  );
+
+  React.useEffect(() => {
+    dispatch(setPVCMetrics(pvcMetrics));
+  }, [dispatch, pvcMetrics]);

This keeps metrics computation tied to Prometheus updates and ensures Redux is updated outside the render phase.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (1)

180-213: Type mismatch: VolumeSnapshotContentPageProps is missing showTitle

VolumeSnapshotContentPage destructures showTitle with a default, but VolumeSnapshotContentPageProps only declares canCreate?: boolean (plus PageComponentProps). This will not type-check because showTitle is not part of the props type.

Add showTitle?: boolean to VolumeSnapshotContentPageProps (or drop the prop from the component) to keep the type definition aligned with usage.

 type VolumeSnapshotContentPageProps = {
-  canCreate?: boolean;
-} & PageComponentProps;
+  canCreate?: boolean;
+  showTitle?: boolean;
+} & PageComponentProps;
🧹 Nitpick comments (4)
frontend/public/components/persistent-volume-claim.tsx (1)

510-533: Confirm initPath behavior when namespace is undefined

initPath currently falls back to the default namespace:

const initPath = `/k8s/ns/${namespace || 'default'}/persistentvolumeclaims/`;

If PersistentVolumeClaimsPage is ever used without an explicit namespace, this will route create links into the default namespace rather than an all-namespaces/cluster-scoped path (if that was the previous behavior).

If the intended behavior is “all namespaces” when namespace is unset, consider something like:

const initPath = namespace
  ? `/k8s/ns/${namespace}/persistentvolumeclaims/`
  : '/k8s/all-namespaces/persistentvolumeclaims/';

Please double-check this against existing PVC pages to avoid changing navigation semantics unexpectedly.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (1)

35-43: Row/column mapping is sound; consider aligning size formatting with VolumeSnapshot

The data-view rows correctly map all tableColumnInfo ids to cells, with sensible fallbacks (DASH) and appropriate use of ResourceLink, Status, Timestamp, and LazyActionMenu. For consistency with VolumeSnapshot sizing, you may want to confirm that status.restoreSize is already in base bytes; if not, consider going through convertToBaseValue before humanizeBinaryBytes to avoid any subtle formatting differences.

If restoreSize is not guaranteed to be base bytes, please double‑check current usage of humanizeBinaryBytes with similar fields elsewhere in the codebase to decide whether a convertToBaseValue step is needed here.

Also applies to: 45-103, 105-156

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (2)

63-146: Guard against future column/rowCells drift in getDataViewRows

The current implementation assumes every columns entry has a corresponding rowCells[id], and uses rowCells[id].disabled in the filter predicate. That’s fine today because both are derived from the same tableColumnInfo, but it’s brittle to future changes (e.g., adding a column but forgetting to extend rowCells would cause a runtime error).

You can make this more robust with optional chaining and a default:

-    return columns
-      .filter(({ id }) => !rowCells[id].disabled)
-      .map(({ id }) => {
-        const cell = rowCells[id]?.cell || DASH;
-        const props = rowCells[id]?.props || undefined;
+    return columns
+      .filter(({ id }) => !rowCells[id]?.disabled)
+      .map(({ id }) => {
+        const cell = rowCells[id]?.cell || DASH;
+        const props = rowCells[id]?.props || undefined;
         return {
           id,
           props,
           cell,
         };
       });

335-369: PVC page behavior preserved; consider making ns optional in the type

VolumeSnapshotPVCPage preserves the existing behavior of preferring the ns prop and falling back to params.ns, and reuses VolumeSnapshotTable so the new data‑view behavior is applied consistently.

Given that the component already handles a missing ns by reading from route params, you may want to reflect that in the type by making ns optional for accuracy:

-type VolumeSnapshotPVCPage = {
-  obj: PersistentVolumeClaimKind;
-  ns: string;
-};
+type VolumeSnapshotPVCPage = {
+  obj: PersistentVolumeClaimKind;
+  ns?: string;
+};

This isn’t required for runtime correctness but better documents the actual call pattern.

Also applies to: 382-385

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 96cafe4 and ab0aeb5.

📒 Files selected for processing (6)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (4 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (4 hunks)
  • frontend/public/components/persistent-volume-claim.tsx (12 hunks)
  • frontend/public/components/persistent-volume.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • frontend/public/components/persistent-volume-claim.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • frontend/public/components/persistent-volume.tsx
🔇 Additional comments (4)
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (2)

30-84: Data-view row mapping and column definitions look consistent and safe

tableColumnInfo, getDataViewRows, and useVolumeSnapshotClassColumns use the same column ids and provide sane fallbacks (DASH) plus sticky name/actions columns. The LazyActionMenu context and default-class badge logic are wired correctly; no issues from a correctness or maintainability standpoint.

Also applies to: 86-119


121-140: ConsoleDataView integration and page wiring are correct

Wrapping ConsoleDataView in Suspense with LoadingBox is appropriate, and loadError is passed through via ...props. The page’s useK8sWatchResource call feeds data, loaded, and loadError into VolumeSnapshotClassTable, and ListPageCreate correctly uses the shared kind constant.

Also applies to: 143-177, 186-190

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (2)

148-215: Column configuration and snapshot-content visibility logic look good

Using rowData.hideSnapshotContentColumn to both (a) mark the column definition as disabled and (b) filter it out via .filter((c) => !c.disabled) cleanly ties column visibility to the FLAGS.CAN_LIST_VSC flag. Sticky name/actions columns and the sort keys align with the row data, so the column setup is coherent and maintainable.


217-295: Status filter integration with ConsoleDataView is correctly wired

The status checkbox filter is correctly defined (filterId="status" and matching VolumeSnapshotFilters['status']), initialFilters extends initialFiltersDefault with status: [], and matchesAdditionalFilters cleanly short‑circuits when a status filter is active. This is a straightforward and maintainable use of the ResourceFilters + additionalFilterNodes pattern.

Also applies to: 371-373

Comment on lines +104 to +175
const getDataViewRows: GetDataViewRows<PersistentVolumeClaimKind> = (data, columns) => {
/* eslint-disable react-hooks/rules-of-hooks */
const { t } = useTranslation();
const pvcMetrics = useSelector<RootState, PVCMetrics>(({ UI }) => UI.getIn(['metrics', 'pvc']));
/* eslint-enable react-hooks/rules-of-hooks */

const kind = 'PersistentVolumeClaim';
return data.map(({ obj }) => {
const metrics = pvcMetrics?.usedCapacity?.[getNamespace(obj)]?.[getName(obj)];
const [name, namespace] = [getName(obj), getNamespace(obj)];
const totalCapacityMetric = convertToBaseValue(obj.status?.capacity?.storage);
const totalCapcityHumanized = humanizeBinaryBytes(totalCapacityMetric);
const usedCapacity = humanizeBinaryBytes(metrics);
const context = { [referenceFor(obj)]: obj };

const PVCTableRow: React.FC<PVCTableRowProps> = ({ obj }) => {
const metrics = useSelector<RootState, PVCMetrics>(
({ UI }) => UI.getIn(['metrics', 'pvc'])?.usedCapacity?.[getNamespace(obj)]?.[getName(obj)],
);
const [name, namespace] = [getName(obj), getNamespace(obj)];
const totalCapacityMetric = convertToBaseValue(obj?.status?.capacity?.storage);
const totalCapcityHumanized = humanizeBinaryBytes(totalCapacityMetric);
const usedCapacity = humanizeBinaryBytes(metrics);
const { t } = useTranslation();
const resourceKind = referenceFor(obj);
const context = { [resourceKind]: obj };
return (
<>
<TableData className={tableColumnClasses[0]}>
<ResourceLink kind={kind} name={name} namespace={namespace} title={name} />
</TableData>
<TableData className={css(tableColumnClasses[1], 'co-break-word')} columnID="namespace">
<ResourceLink kind="Namespace" name={namespace} title={namespace} />
</TableData>
<TableData className={tableColumnClasses[2]}>
<PVCStatusComponent pvc={obj} />
</TableData>
<TableData className={tableColumnClasses[3]}>
{_.get(obj, 'spec.volumeName') ? (
const rowCells = {
[tableColumnInfo[0].id]: {
cell: <ResourceLink kind={kind} name={name} namespace={namespace} title={name} />,
props: getNameCellProps(name),
},
[tableColumnInfo[1].id]: {
cell: <ResourceLink kind="Namespace" name={namespace} title={namespace} />,
},
[tableColumnInfo[2].id]: {
cell: <PVCStatusComponent pvc={obj} />,
},
[tableColumnInfo[3].id]: {
cell: obj.spec?.volumeName ? (
<ResourceLink
kind="PersistentVolume"
name={obj.spec.volumeName}
title={obj.spec.volumeName}
/>
) : (
<div className="pf-v6-u-text-color-subtle">{t('public~No PersistentVolume')}</div>
)}
</TableData>
<TableData className={tableColumnClasses[4]}>
{totalCapacityMetric ? totalCapcityHumanized.string : '-'}
</TableData>
<TableData className={tableColumnClasses[5]}>{metrics ? usedCapacity.string : '-'}</TableData>
<TableData className={css(tableColumnClasses[6])}>
{obj?.spec?.storageClassName ? (
),
},
[tableColumnInfo[4].id]: {
cell: totalCapacityMetric ? totalCapcityHumanized.string : DASH,
},
[tableColumnInfo[5].id]: {
cell: metrics ? usedCapacity.string : DASH,
},
[tableColumnInfo[6].id]: {
cell: obj.spec?.storageClassName ? (
<ResourceLink
kind="StorageClass"
name={obj.spec.storageClassName}
title={obj.spec.storageClassName}
/>
) : (
'-'
)}
</TableData>
<TableData className={tableColumnClasses[7]}>
<LazyActionMenu context={context} />
</TableData>
</>
DASH
),
},
[tableColumnInfo[7].id]: {
cell: <LazyActionMenu context={context} />,
props: {
...actionsCellProps,
},
},
};

return columns.map(({ id }) => {
const cell = rowCells[id]?.cell || DASH;
const props = rowCells[id]?.props || undefined;
return {
id,
props,
cell,
};
});
});
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Avoid calling Hooks inside getDataViewRows – lift them to the list component

getDataViewRows calls useTranslation and useSelector and suppresses react-hooks/rules-of-hooks. This breaks the normal Hooks contract and makes the callback fragile if its call pattern ever changes.

Refactor so that Hooks are called in PersistentVolumeClaimList (or a custom hook) and their results are captured in a memoized callback passed to ConsoleDataView, e.g.:

-const getDataViewRows: GetDataViewRows<PersistentVolumeClaimKind> = (data, columns) => {
-  /* eslint-disable react-hooks/rules-of-hooks */
-  const { t } = useTranslation();
-  const pvcMetrics = useSelector<RootState, PVCMetrics>(({ UI }) => UI.getIn(['metrics', 'pvc']));
-  /* eslint-enable react-hooks/rules-of-hooks */
-  // row-building logic...
-};
+// inside PersistentVolumeClaimList
+const pvcMetrics = useSelector<RootState, PVCMetrics>(({ UI }) => UI.getIn(['metrics', 'pvc']));
+
+const getDataViewRows = React.useCallback<GetDataViewRows<PersistentVolumeClaimKind>>(
+  (data, columns) => {
+    // existing row-building logic, but using captured `t` and `pvcMetrics`
+  },
+  [t, pvcMetrics],
+);

Then pass this memoized getDataViewRows into <ConsoleDataView /> instead of the current top-level constant.

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +55 to +58
const getDataViewRows: GetDataViewRows<PersistentVolumeKind> = (data, columns) => {
/* eslint-disable react-hooks/rules-of-hooks */
const { t } = useTranslation();
/* eslint-enable react-hooks/rules-of-hooks */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify Rules of Hooks compliance in getDataViewRows.

The getDataViewRows function calls useTranslation() but is neither a React component nor a custom hook (doesn't start with "use"). This violates the Rules of Hooks, which state hooks can only be called from function components or custom hooks. While the eslint-disable comment indicates this is intentional, it could cause runtime issues if getDataViewRows is invoked outside a render context.

Alternatively, if this pattern is unsafe, consider refactoring to:

  1. Convert getDataViewRows to a custom hook: useDataViewRows
  2. Extract translation keys and pass pre-translated strings
  3. Move hook calls to the parent component

🏁 Script executed:

#!/bin/bash
# Verify how ConsoleDataView invokes getDataViewRows to confirm hook usage is safe
ast-grep --pattern $'function ConsoleDataView($$$) {
  $$$
  getDataViewRows($$$)
  $$$
}'

# Also check the GetDataViewRows type definition for guidance
ast-grep --pattern 'type GetDataViewRows = $$$'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find where ConsoleDataView is imported from
rg -n "ConsoleDataView" frontend/public/components/persistent-volume.tsx -A2 -B2

# Find where GetDataViewRows type is defined
rg -n "type GetDataViewRows" --type=ts --type=tsx

# Find the ConsoleDataView definition
fd -e tsx -e ts | xargs rg -l "export.*ConsoleDataView" | head -5

Length of output: 901


🏁 Script executed:

#!/bin/bash
# Search for other uses of getDataViewRows pattern in codebase to understand if it's widely used
rg -n "useTranslation.*eslint-disable" --type=tsx --type=ts -B3 -A3 | head -50

# Also check how getDataViewRows callbacks are structured elsewhere
rg -n "GetDataViewRows<" --type=tsx --type=ts -B2 -A10 | head -100

Length of output: 55


🏁 Script executed:

#!/bin/bash
# View the GetDataViewRows type definition
cat frontend/packages/console-app/src/components/data-view/types.ts | head -100

# View ConsoleDataView implementation (first 150 lines to understand how it uses getDataViewRows)
cat frontend/packages/console-app/src/components/data-view/ConsoleDataView.tsx | head -150

Length of output: 5330


🏁 Script executed:

#!/bin/bash
# Search for other instances of this eslint-disable pattern with useTranslation in getDataViewRows-like functions
rg -n "getDataViewRows" --no-filename -A10 | grep -A10 "useTranslation"

Length of output: 7883


🏁 Script executed:

#!/bin/bash
# Verify the exact location where getDataViewRows is called in useConsoleDataViewData
grep -n "getDataViewRows" frontend/packages/console-app/src/components/data-view/useConsoleDataViewData.tsx -B5 -A5

Length of output: 969


Rules of Hooks violation confirmed—refactor getDataViewRows to a custom hook or pass translation as a parameter.

The getDataViewRows function calls useTranslation() (lines 56-58) but is neither a React component nor a custom hook. Although it's invoked within the useConsoleDataViewData custom hook (at line 152 in useConsoleDataViewData.tsx), the function itself violates the Rules of Hooks. The eslint-disable suppresses a legitimate warning.

Fix by either:

  1. Renaming to usePersistentVolumeDataViewRows and moving useTranslation() call into it
  2. Extracting the translation at the parent component level and passing the t function or pre-translated strings to getDataViewRows
🤖 Prompt for AI Agents
In frontend/public/components/persistent-volume.tsx around lines 55 to 58,
getDataViewRows illegally calls useTranslation() even though it is not a React
hook — remove the eslint-disable and fix by either (A) converting it to a proper
hook: rename to usePersistentVolumeDataViewRows, move the useTranslation() call
inside that new hook, adjust its signature/return to match callers, update
imports/usages to call the hook from components/custom hooks only; or (B)
eliminate the hook call by extracting t at the parent level and changing
getDataViewRows to accept a t parameter (or fully translated strings), remove
useTranslation() from this file, and update all call sites to pass the t
function/strings accordingly.

@vojtechszocs vojtechszocs force-pushed the data-view-storage-tables branch from ab0aeb5 to f312ffb Compare November 21, 2025 18:40
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Nov 21, 2025

@vojtechszocs: This pull request references CONSOLE-4723 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.21.0" version, but no target version was set.

In response to this:

Depends on #15713

  • packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
  • packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • public/components/persistent-volume-claim.tsx
  • public/components/persistent-volume.tsx
  • public/components/storage-class.tsx
  • public/components/volumes-table.tsx

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (1)

151-161: VolumeSnapshotClass is cluster‑scoped but watched as namespaced

VolumeSnapshotClass is a cluster‑scoped resource; configuring useK8sWatchResource with namespaced: true and a namespace can result in watching the wrong URL (or an empty list), especially compared to VolumeSnapshotContentPage, which omits namespaced for another cluster‑scoped type.

Consider updating the watcher to treat it as cluster‑scoped, e.g.:

 const [resources, loaded, loadError] = useK8sWatchResource<VolumeSnapshotClassKind[]>({
   groupVersionKind: {
     group: VolumeSnapshotClassModel.apiGroup,
     kind: VolumeSnapshotClassModel.kind,
     version: VolumeSnapshotClassModel.apiVersion,
   },
   isList: true,
-  namespaced: true,
-  namespace,
+  namespaced: false,
   selector,
 });

and ignoring the namespace prop for this page if it is not actually used anywhere else.

♻️ Duplicate comments (2)
frontend/public/components/persistent-volume.tsx (1)

55-113: Rules of Hooks violation remains unresolved.

The getDataViewRows function still calls useTranslation() (line 57) despite not being a React component or custom hook. This was flagged in a previous review with suggested fixes. Please address this critical issue before merging.

frontend/public/components/persistent-volume-claim.tsx (1)

104-175: Rules of Hooks violation remains unresolved.

The getDataViewRows function calls both useTranslation() (line 106) and useSelector() (line 107) despite not being a React component or custom hook. This was flagged in a previous review with a detailed refactoring suggestion. Please address this critical issue before merging.

🧹 Nitpick comments (3)
frontend/public/components/storage-class.tsx (1)

252-260: Improve type safety in type definitions.

Two type-related improvements:

  1. Line 259: obj: any should use a more specific type like StorageClassResourceKind for better type safety.
  2. Line 255: loadError is declared in StorageClassListProps but not used in the component at line 160. Either use it (e.g., for error handling in ConsoleDataView) or remove it from the type definition.

Apply this diff to improve type safety:

 export type StorageClassDetailsProps = {
-  obj: any;
+  obj: StorageClassResourceKind;
 };
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (2)

63-66: Make row/column mapping resilient to future column changes

getDataViewRows filters with rowCells[id].disabled:

return columns
  .filter(({ id }) => !rowCells[id].disabled)
  .map(({ id }) => {
    const cell = rowCells[id]?.cell || DASH;
    const props = rowCells[id]?.props || undefined;
    ...
  });

This assumes every columns entry has a corresponding rowCells[id]. If a new column is ever added and rowCells isn’t updated, this will throw at runtime before the optional chaining is reached.

A small defensive tweak avoids that brittleness:

-  .filter(({ id }) => !rowCells[id].disabled)
+  .filter(({ id }) => !rowCells[id]?.disabled)

You may also not need the disabled flag on rowCells at all, since useVolumeSnapshotColumns already filters disabled columns at the header level.

Also applies to: 82-96, 105-132, 134-146


148-215: Status filter wiring looks good; consider centralizing status values

The status checkbox options and matchesAdditionalFilters logic are consistent and should work as intended, assuming volumeSnapshotStatus() returns "Ready" | "Pending" | "Error" for the cases you care about.

To reduce the chance of divergence if statuses evolve, consider sharing constants (or a small enum/union) between volumeSnapshotStatus and volumeSnapshotStatusFilterOptions rather than hardcoding the strings in two places.

Also applies to: 221-226, 227-276, 279-293

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between ab0aeb5 and f312ffb.

📒 Files selected for processing (7)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (4 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (3 hunks)
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (4 hunks)
  • frontend/public/components/persistent-volume-claim.tsx (12 hunks)
  • frontend/public/components/persistent-volume.tsx (3 hunks)
  • frontend/public/components/storage-class.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/packages/console-app/src/components/nodes/NodesPage.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**

⚙️ CodeRabbit configuration file

-Focus on major issues impacting performance, readability, maintainability and security. Avoid nitpicks and avoid verbosity.

Files:

  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx
  • frontend/public/components/storage-class.tsx
  • frontend/public/components/persistent-volume-claim.tsx
  • frontend/public/components/persistent-volume.tsx
  • frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
🔇 Additional comments (4)
frontend/public/components/storage-class.tsx (1)

211-230: Verify intention to omit filter toolbar.

Line 225 sets omitFilterToolbar={true}, which removes filtering capabilities from the storage class list. If this is part of the migration strategy, please confirm this is intentional, as it may affect user experience.

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsx (1)

41-84: Data-view migration for VolumeSnapshotClass table looks consistent

Row construction, column IDs, and use of DASH fallback are coherent with the shared data‑view patterns. Sticky name/actions columns and memoized column definitions should keep the table behavior and UX consistent with other data‑view based lists.

Also applies to: 86-119, 121-139

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx (1)

54-103: ConsoleDataView integration for VolumeSnapshotContent looks solid

The row mapping, column definitions, Suspense/LoadingBox wrapper, and use of DASH fallback and actions cell props are consistent with the new data‑view patterns and should preserve behavior while simplifying the table/rendering code.

Also applies to: 105-156, 158-177, 180-205

frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (1)

335-338: PVC‑scoped reuse of VolumeSnapshotTable looks correct

checkPVCSnapshot correctly filters snapshots by PVC name and namespace, and VolumeSnapshotPVCPage reuses VolumeSnapshotTable with the filtered data while preserving loaded/loadError wiring. This keeps the PVC view aligned with the main snapshot list behavior.

Also applies to: 345-346, 362-366

Comment on lines +45 to 53
const getDataViewRows: GetDataViewRows<VolumeSnapshotContentKind> = (data, columns) => {
return data.map(({ obj }) => {
const name = obj.metadata?.name || '';
const creationTimestamp = obj.metadata?.creationTimestamp || '';
const snapshotName = obj.spec?.volumeSnapshotRef?.name || '';
const snapshotNamespace = obj.spec?.volumeSnapshotRef?.namespace || '';
const size = obj.status?.restoreSize;
const sizeMetrics = size ? humanizeBinaryBytes(size).string : DASH;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix VolumeSnapshotContent size humanization to use base bytes

obj.status?.restoreSize is a quantity (e.g., 10Gi), but it’s passed directly into humanizeBinaryBytes. In volume-snapshot.tsx you correctly call convertToBaseValue first; without that, this can produce incorrect or NaN values.

Align this code with the snapshot list:

-import { humanizeBinaryBytes } from '@console/internal/components/utils/units';
+import { convertToBaseValue, humanizeBinaryBytes } from '@console/internal/components/utils/units';
...
-    const size = obj.status?.restoreSize;
-    const sizeMetrics = size ? humanizeBinaryBytes(size).string : DASH;
+    const size = obj.status?.restoreSize;
+    const sizeBase = convertToBaseValue(size);
+    const sizeMetrics = size ? humanizeBinaryBytes(sizeBase).string : DASH;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getDataViewRows: GetDataViewRows<VolumeSnapshotContentKind> = (data, columns) => {
return data.map(({ obj }) => {
const name = obj.metadata?.name || '';
const creationTimestamp = obj.metadata?.creationTimestamp || '';
const snapshotName = obj.spec?.volumeSnapshotRef?.name || '';
const snapshotNamespace = obj.spec?.volumeSnapshotRef?.namespace || '';
const size = obj.status?.restoreSize;
const sizeMetrics = size ? humanizeBinaryBytes(size).string : DASH;
const getDataViewRows: GetDataViewRows<VolumeSnapshotContentKind> = (data, columns) => {
return data.map(({ obj }) => {
const name = obj.metadata?.name || '';
const creationTimestamp = obj.metadata?.creationTimestamp || '';
const snapshotName = obj.spec?.volumeSnapshotRef?.name || '';
const snapshotNamespace = obj.spec?.volumeSnapshotRef?.namespace || '';
const size = obj.status?.restoreSize;
const sizeBase = convertToBaseValue(size);
const sizeMetrics = size ? humanizeBinaryBytes(sizeBase).string : DASH;
🤖 Prompt for AI Agents
In
frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsx
around lines 45 to 53, the code passes obj.status?.restoreSize (a k8s Quantity
like "10Gi") directly to humanizeBinaryBytes which yields incorrect/NaN results;
change this to first convert the Quantity to base bytes using
convertToBaseValue(obj.status?.restoreSize) and then pass that numeric base
value to humanizeBinaryBytes, and add the necessary import for
convertToBaseValue if not already present.

Comment on lines +68 to +123
const getDataViewRows: GetDataViewRows<StorageClassResourceKind> = (data, columns) => {
/* eslint-disable react-hooks/rules-of-hooks */
const { t } = useTranslation();
/* eslint-enable react-hooks/rules-of-hooks */

const StorageClassDetails: React.FC<StorageClassDetailsProps> = ({ obj }) => {
const isKubevirtPluginActive =
Array.isArray(window.SERVER_FLAGS.consolePlugins) &&
window.SERVER_FLAGS.consolePlugins.includes('kubevirt-plugin');

return data.map(({ obj }) => {
const name = obj.metadata?.name || '';
const context = { [referenceFor(obj)]: obj };

const rowCells = {
[tableColumnInfo[0].id]: {
cell: (
<ResourceLink kind={kind} name={name}>
{isDefaultClass(obj) && (
<span className="pf-v6-u-font-size-xs pf-v6-u-text-color-subtle co-resource-item__help-text">
&ndash; {t('public~Default')}
</span>
)}
{isDefaultVirtClass(obj) && isKubevirtPluginActive && (
<span className="pf-v6-u-font-size-xs pf-v6-u-text-color-subtle co-resource-item__help-text">
&ndash; {t('public~Default for VirtualMachines')}
</span>
)}
</ResourceLink>
),
props: getNameCellProps(name),
},
[tableColumnInfo[1].id]: {
cell: obj.provisioner,
},
[tableColumnInfo[2].id]: {
cell: obj.reclaimPolicy || DASH,
},
[tableColumnInfo[3].id]: {
cell: <LazyActionMenu context={context} />,
props: {
...actionsCellProps,
},
},
};

return columns.map(({ id }) => {
const cell = rowCells[id]?.cell || DASH;
const props = rowCells[id]?.props || undefined;
return {
id,
props,
cell,
};
});
});
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Fix hook usage violation and verify data structure consistency.

This function has two significant issues:

  1. Critical: Lines 69-71 violate React's Rules of Hooks. useTranslation() can only be called at the top level of React components or custom hooks. Calling it inside getDataViewRows (which is a regular function passed as a prop) will cause runtime errors.

  2. Major: Line 77 destructures { obj } from data items, but the type definition at line 253 declares data: StorageClassResourceKind[] (not { obj: StorageClassResourceKind }[]). This type inconsistency needs verification.

Solution for the hook violation:

Move the translation hook to a parent scope. One approach is to modify getDataViewRows to accept t as a parameter:

-const getDataViewRows: GetDataViewRows<StorageClassResourceKind> = (data, columns) => {
-  /* eslint-disable react-hooks/rules-of-hooks */
-  const { t } = useTranslation();
-  /* eslint-enable react-hooks/rules-of-hooks */
+const getDataViewRows = (data, columns, t) => {

Then pass t from StorageClassList:

 export const StorageClassList: React.FCC<StorageClassListProps> = ({ data, loaded, ...props }) => {
   const columns = useStorageClassColumns();
+  const { t } = useTranslation();

   return (
     <React.Suspense fallback={<LoadingBox />}>
       <ConsoleDataView<StorageClassResourceKind>
         {...props}
         label={StorageClassModel.labelPlural}
         data={data}
         loaded={loaded}
         columns={columns}
-        getDataViewRows={getDataViewRows}
+        getDataViewRows={(data, columns) => getDataViewRows(data, columns, t)}
         hideColumnManagement
       />
     </React.Suspense>
   );
 };

Verify the data structure:

Please run the following script to check how GetDataViewRows is defined and what data structure it expects:


🏁 Script executed:

#!/bin/bash
# Check the GetDataViewRows type definition
ast-grep --pattern 'type GetDataViewRows$$$= $$$'

# Check ConsoleDataView usage patterns
rg -nP -A5 'getDataViewRows.*=.*\(.*data.*columns' --type=tsx

Length of output: 27



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for GetDataViewRows type definition using correct file type
rg -nP 'type GetDataViewRows|GetDataViewRows\s*=' --type=ts

# Also search for ConsoleDataView interface to understand the parameter types
rg -nP 'interface ConsoleDataView|getDataViewRows' --type=ts -A2 -B2

Length of output: 51230



---

🏁 Script executed:

```shell
#!/bin/bash
# Get full type definition of GetDataViewRows
rg -nP -A10 'export type GetDataViewRows' frontend/packages/console-app/src/components/data-view/types.ts

# View storage-class.tsx around line 253 to see type declaration
sed -n '250,260p' frontend/public/components/storage-class.tsx

# Look for RowProps definition
rg -nP 'type RowProps|interface RowProps' --type=ts -A3

Length of output: 785


Fix critical hook usage violation and type inconsistency in data structure.

Two issues require immediate attention:

  1. Critical (lines 69-71): Calling hooks within nested functions violates React Rules of Hooks. useTranslation() must be called at the component level, not inside getDataViewRows.

  2. Major (line 77 & type mismatch): The function destructures { obj } from data, but StorageClassListProps declares data: StorageClassResourceKind[] (line 253). The actual expected type is data: RowProps<StorageClassResourceKind>[] based on the GetDataViewRows signature, which defines the parameter as data: RowProps<TData>[].

Recommended fix for the hook violation:

Pass t as a parameter from the parent component:

-const getDataViewRows: GetDataViewRows<StorageClassResourceKind> = (data, columns) => {
-  /* eslint-disable react-hooks/rules-of-hooks */
-  const { t } = useTranslation();
-  /* eslint-enable react-hooks/rules-of-hooks */
+const getDataViewRows: GetDataViewRows<StorageClassResourceKind> = (data, columns, t) => {

Then in StorageClassList, call the hook at the component level and pass it down:

 export const StorageClassList: React.FCC<StorageClassListProps> = ({ data, loaded, ...props }) => {
   const columns = useStorageClassColumns();
+  const { t } = useTranslation();

   return (
     <React.Suspense fallback={<LoadingBox />}>
       <ConsoleDataView<StorageClassResourceKind>
         {...props}
         label={StorageClassModel.labelPlural}
         data={data}
         loaded={loaded}
         columns={columns}
-        getDataViewRows={getDataViewRows}
+        getDataViewRows={(d, c) => getDataViewRows(d, c, t)}
         hideColumnManagement
       />
     </React.Suspense>
   );
 };

Verify the type declaration at line 253 to ensure it matches the actual data structure being passed.

Committable suggestion skipped: line range outside the PR's diff.

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Nov 21, 2025

@vojtechszocs: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-gcp-console f312ffb link true /test e2e-gcp-console
ci/prow/frontend f312ffb link true /test frontend

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. component/core Related to console core functionality component/helm Related to helm-plugin component/monitoring Related to monitoring do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants