-
Notifications
You must be signed in to change notification settings - Fork 665
CONSOLE-4723: Migrate storage related tables to ConsoleDataView #15747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
CONSOLE-4723: Migrate storage related tables to ConsoleDataView #15747
Conversation
|
[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 |
|
@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. |
WalkthroughMade ConsoleDataView's Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Areas to pay extra attention:
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
|
/hold |
|
@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. |
|
@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. |
There was a problem hiding this 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 consistencyHere you call
humanizeBinaryBytes(size).stringdirectly onobj.status?.restoreSize. Involume-snapshot.tsx, size is first normalized withconvertToBaseValuebefore 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: MakegetDataViewRowsresilient to missing rowDataRight now you destructure
rowData: { hideSnapshotContentColumn }directly from each element. This assumesrowDatais always present, which is true given the currentcustomRowDatausage, but it makes the helper fragile to future reuse. A small tweak to handle an optionalrowData(e.g.,const hideSnapshotContentColumn = rowData?.hideSnapshotContentColumn;) would make this safer without changing behavior today.
227-275: Confirm status filter values stay aligned withvolumeSnapshotStatusThe status filter options (
Ready,Pending,Error) are hard-coded, andmatchesAdditionalFilterschecksvolumeSnapshotStatus(resource)against these strings. This is fine as long asvolumeSnapshotStatuscontinues 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
📒 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.tsxfrontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsxfrontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-content.tsxfrontend/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
initialFiltersprevents 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 robustThe new
getDataViewRowsimplementation builds row cells fromVolumeSnapshotContentKindcleanly, usesDASHfallbacks, and wires actions/name/status/links in a data-driven way that should play nicely withConsoleDataViewand 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 solidThe combination of
getDataViewRows,useVolumeSnapshotClassColumns, and theConsoleDataViewwrapper preserves existing behavior (default-class badge, driver/deletionPolicy display, actions) while standardizing on the new data-view patterns. Row construction, column IDs, andDASHfallbacks 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
getDataViewRowscorrectly derives name/namespace/status/size/source/content/class/timestamp and wires actions/context. ThehideSnapshotContentColumnflag is applied consistently in both the column definitions and the per-rowdisabledfield, so the snapshot content column should cleanly disappear whenCAN_LIST_VSCis false without leaving empty cells.
8f69bf3 to
96cafe4
Compare
There was a problem hiding this 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: ForwardcanCreateandshowTitleprops toListPagecomponentThe review comment is accurate.
PersistentVolumeClaimsPageProps(lines 590-594) definescanCreate?andshowTitle?as optional properties, but the component destructures onlynamespaceand spreads remaining props without explicitly handling these two. The component then hardcodescanCreate={true}and never forwardsshowTitle, 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
ListPageinstead of hardcodingcanCreate={true}and omittingshowTitle.
489-508: Dispatch in render phase confirmed—infinite render loop risk is real and criticalVerification confirms the review comment is accurate. The code has:
dispatch(setPVCMetrics(pvcMetrics))directly in component body (line 507), not wrapped in any effect- No
useEffectanywhere in the component that might mitigate thispvcMetricsis rebuilt each render via the ternary + reduce (no memoization)dispatchis a fresh function fromuseDispatch()each renderThis creates a render loop: pvcMetrics object → dispatch → store update → re-render → new pvcMetrics object → repeat.
The suggested fix is correct—use
useMemoforpvcMetricsanduseEffectfor the dispatch call with proper dependency arrays.frontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsx (1)
335-369: EnsurecheckPVCSnapshotalways returns an array to the table
checkPVCSnapshotcurrently uses optional chaining:const checkPVCSnapshot = ( volumeSnapshots: VolumeSnapshotKind[], pvc: K8sResourceKind, ): VolumeSnapshotKind[] => volumeSnapshots?.filter(/* ... */);If
volumeSnapshotsis evernull/undefined, this expression returnsundefined, butVolumeSnapshotTableexpectsdata: VolumeSnapshotKind[]. To avoid passingundefinedinto 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
VolumeSnapshotTablecontract.
🧹 Nitpick comments (4)
frontend/public/components/persistent-volume-claim.tsx (2)
103-174: Avoid using hooks directly insidegetDataViewRowsutility
getDataViewRowscallsuseTranslationanduseSelectorwhile explicitly disablingreact-hooks/rules-of-hooks. This is brittle: ifConsoleDataViewever 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 fromPersistentVolumeClaimListtoConsoleDataView. That keeps hooks at the top level of a React hook/component and lets you re‑enable the eslint rule.
584-588: AlignPersistentVolumeClaimListProps['data']type with whatGetDataViewRowsexpects
PersistentVolumeClaimListPropscurrently typesdataasPersistentVolumeClaimKind[], butgetDataViewRowstreats each element as an object with anobjproperty. To avoid type drift and ensure callers pass the correct row shape, it’s safer to derive the element type fromGetDataViewRows:-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 normalizingrestoreSizebefore humanizingIn
getDataViewRows,sizeMetricscallshumanizeBinaryBytes(size)directly, whereas the VolumeSnapshot table converts quantities viaconvertToBaseValuefirst. Ifstatus.restoreSizeis 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 keyIn
isDefaultSnapshotClass, the default annotations object uses a literal keydefaultSnapshotClassAnnotation: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
📒 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.tsxfrontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsxfrontend/public/components/persistent-volume-claim.tsxfrontend/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
rowCellskeys are aligned withtableColumnInfoand thecolumnsIDs, and the fallback toDASHplus optionalpropshandling 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
useVolumeSnapshotContentColumnsdefines stable, memoized columns with appropriate sort keys and sticky/nowrap props, andVolumeSnapshotContentTablepassesdata,loaded,loadError,columns, andgetDataViewRowsintoConsoleDataViewinside aSuspensewithLoadingBoxfallback. This aligns with the new data-view patterns and looks good.
180-206: Page-level watch and table composition look good
VolumeSnapshotContentPagewatches the correct GVK, treats the resource as non‑namespaced, passesloadErrorthrough to the table, and usesListPageCreatewith the sharedkindreference. 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
getDataViewRowscorrectly builds cells for name/driver/deletionPolicy/actions, including the “Default” marker and action menu context, anduseVolumeSnapshotClassColumnsdefines matching columns with sticky/nowrap props.VolumeSnapshotClassTablethen passes these intoConsoleDataViewwithloaded,loadError, and aLoadingBoxfallback. Overall the new data-view implementation is coherent and free of obvious issues.
143-174: Page wiring and creation flow remain intact
VolumeSnapshotClassPagestill watches the correct model, threadsnamespaceandselector, and now uses the sharedkindreference forListPageCreatewhile passingloadErrorto 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
getDataViewRowsbuilds cells for all columns, usesVolumeSnapshotRowData.hideSnapshotContentColumnto mark the SnapshotContent cell as disabled, and correctly falls back toDASHfor missing values. The filter overcolumnsand the use ofreferenceForModel/referenceForfor 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
useVolumeSnapshotColumnscleanly encapsulates column definitions and hides the SnapshotContent column whenhideSnapshotContentColumnis true, whileVolumeSnapshotTablewires incustomRowData, a status checkbox filter, andmatchesAdditionalFiltersto feedConsoleDataView. The use ofinitialFiltersDefaultand memoized filter options keeps configuration centralized and avoids unnecessary re-renders. No blocking issues here.
|
@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. |
|
@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. |
96cafe4 to
ab0aeb5
Compare
|
@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. |
There was a problem hiding this 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: FixuseFlagusage 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
useFlagreturns a single boolean (as elsewhere in Console),canListPVwill always beundefined, and thevolumeName && 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 dispatchingsetPVCMetricsdirectly in render; memoize + useEffect
PersistentVolumeClaimsPagecurrently 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:VolumeSnapshotContentPagePropsis missingshowTitle
VolumeSnapshotContentPagedestructuresshowTitlewith a default, butVolumeSnapshotContentPagePropsonly declarescanCreate?: boolean(plusPageComponentProps). This will not type-check becauseshowTitleis not part of the props type.Add
showTitle?: booleantoVolumeSnapshotContentPageProps(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: ConfirminitPathbehavior whennamespaceis undefined
initPathcurrently falls back to thedefaultnamespace:const initPath = `/k8s/ns/${namespace || 'default'}/persistentvolumeclaims/`;If
PersistentVolumeClaimsPageis ever used without an explicitnamespace, this will route create links into thedefaultnamespace rather than an all-namespaces/cluster-scoped path (if that was the previous behavior).If the intended behavior is “all namespaces” when
namespaceis 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 VolumeSnapshotThe data-view rows correctly map all
tableColumnInfoids to cells, with sensible fallbacks (DASH) and appropriate use ofResourceLink,Status,Timestamp, andLazyActionMenu. For consistency withVolumeSnapshotsizing, you may want to confirm thatstatus.restoreSizeis already in base bytes; if not, consider going throughconvertToBaseValuebeforehumanizeBinaryBytesto avoid any subtle formatting differences.If
restoreSizeis not guaranteed to be base bytes, please double‑check current usage ofhumanizeBinaryByteswith similar fields elsewhere in the codebase to decide whether aconvertToBaseValuestep 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 ingetDataViewRowsThe current implementation assumes every
columnsentry has a correspondingrowCells[id], and usesrowCells[id].disabledin thefilterpredicate. That’s fine today because both are derived from the sametableColumnInfo, but it’s brittle to future changes (e.g., adding a column but forgetting to extendrowCellswould 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 makingnsoptional in the type
VolumeSnapshotPVCPagepreserves the existing behavior of preferring thensprop and falling back toparams.ns, and reusesVolumeSnapshotTableso the new data‑view behavior is applied consistently.Given that the component already handles a missing
nsby reading from route params, you may want to reflect that in the type by makingnsoptional 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
📒 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.tsxfrontend/public/components/persistent-volume-claim.tsxfrontend/packages/console-app/src/components/volume-snapshot/volume-snapshot.tsxfrontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsxfrontend/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, anduseVolumeSnapshotClassColumnsuse 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 correctWrapping
ConsoleDataViewinSuspensewithLoadingBoxis appropriate, andloadErroris passed through via...props. The page’suseK8sWatchResourcecall feedsdata,loaded, andloadErrorintoVolumeSnapshotClassTable, andListPageCreatecorrectly uses the sharedkindconstant.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 goodUsing
rowData.hideSnapshotContentColumnto both (a) mark the column definition as disabled and (b) filter it out via.filter((c) => !c.disabled)cleanly ties column visibility to theFLAGS.CAN_LIST_VSCflag. 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 wiredThe status checkbox filter is correctly defined (
filterId="status"and matchingVolumeSnapshotFilters['status']),initialFiltersextendsinitialFiltersDefaultwithstatus: [], andmatchesAdditionalFilterscleanly short‑circuits when a status filter is active. This is a straightforward and maintainable use of theResourceFilters+additionalFilterNodespattern.Also applies to: 371-373
| 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, | ||
| }; | ||
| }); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
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.
| const getDataViewRows: GetDataViewRows<PersistentVolumeKind> = (data, columns) => { | ||
| /* eslint-disable react-hooks/rules-of-hooks */ | ||
| const { t } = useTranslation(); | ||
| /* eslint-enable react-hooks/rules-of-hooks */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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:
- Convert
getDataViewRowsto a custom hook:useDataViewRows - Extract translation keys and pass pre-translated strings
- 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 -5Length 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 -100Length 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 -150Length 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 -A5Length 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:
- Renaming to
usePersistentVolumeDataViewRowsand movinguseTranslation()call into it - Extracting the translation at the parent component level and passing the
tfunction or pre-translated strings togetDataViewRows
🤖 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.
ab0aeb5 to
f312ffb
Compare
|
@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. |
There was a problem hiding this 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
VolumeSnapshotClassis a cluster‑scoped resource; configuringuseK8sWatchResourcewithnamespaced: trueand anamespacecan result in watching the wrong URL (or an empty list), especially compared toVolumeSnapshotContentPage, which omitsnamespacedfor 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
namespaceprop 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
getDataViewRowsfunction still callsuseTranslation()(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
getDataViewRowsfunction calls bothuseTranslation()(line 106) anduseSelector()(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:
- Line 259:
obj: anyshould use a more specific type likeStorageClassResourceKindfor better type safety.- Line 255:
loadErroris declared inStorageClassListPropsbut 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
getDataViewRowsfilters withrowCells[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
columnsentry has a correspondingrowCells[id]. If a new column is ever added androwCellsisn’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
disabledflag onrowCellsat all, sinceuseVolumeSnapshotColumnsalready 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 valuesThe status checkbox options and
matchesAdditionalFilterslogic are consistent and should work as intended, assumingvolumeSnapshotStatus()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
volumeSnapshotStatusandvolumeSnapshotStatusFilterOptionsrather 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
📒 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.tsxfrontend/packages/console-app/src/components/volume-snapshot/volume-snapshot-class.tsxfrontend/public/components/storage-class.tsxfrontend/public/components/persistent-volume-claim.tsxfrontend/public/components/persistent-volume.tsxfrontend/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 consistentRow construction, column IDs, and use of
DASHfallback 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 solidThe row mapping, column definitions, Suspense/LoadingBox wrapper, and use of
DASHfallback 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
checkPVCSnapshotcorrectly filters snapshots by PVC name and namespace, andVolumeSnapshotPVCPagereusesVolumeSnapshotTablewith the filtered data while preservingloaded/loadErrorwiring. This keeps the PVC view aligned with the main snapshot list behavior.Also applies to: 345-346, 362-366
| 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; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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"> | ||
| – {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"> | ||
| – {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, | ||
| }; | ||
| }); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Fix hook usage violation and verify data structure consistency.
This function has two significant issues:
-
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 insidegetDataViewRows(which is a regular function passed as a prop) will cause runtime errors. -
Major: Line 77 destructures
{ obj }from data items, but the type definition at line 253 declaresdata: 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=tsxLength 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:
-
Critical (lines 69-71): Calling hooks within nested functions violates React Rules of Hooks.
useTranslation()must be called at the component level, not insidegetDataViewRows. -
Major (line 77 & type mismatch): The function destructures
{ obj }from data, butStorageClassListPropsdeclaresdata: StorageClassResourceKind[](line 253). The actual expected type isdata: RowProps<StorageClassResourceKind>[]based on theGetDataViewRowssignature, which defines the parameter asdata: 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.
|
@vojtechszocs: The following tests failed, say
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. |
Depends on #15713