Skip to content

Conversation

@sg00dwin
Copy link
Member

@sg00dwin sg00dwin commented Feb 11, 2026

This pull request migrates two OLM modals from the legacy createModalLauncher pattern and React Router v5 history.push() to the modern OverlayComponent pattern with useNavigate(). This migration is part of CONSOLE-5012 (modal migration) and removes blockers for the React Router v7 upgrade (CONSOLE-5049).

Changes Made

Modals Migrated (2 total)

1. DeleteResourceModal - Deletes operator-managed resources

Files Modified

DeleteResourceModal:

  1. packages/operator-lifecycle-manager/src/components/modals/DeleteResourceModal.tsx

    • Removed createModalLauncher import and export
    • Added useOverlay import
    • Exported component as DeleteResourceOverlay with OverlayComponent type
    • Wrapped modal in ModalWrapper with proper prop spreading
    • Added lazy-loaded export to modals/index.ts
  2. packages/operator-lifecycle-manager/src/components/modals/index.ts (created)

    • Added lazy-loaded exports for both modals:
      • LazyDeleteResourceOverlay
      • LazyUninstallOperatorModalOverlay
    • Implemented code splitting with webpack chunk names
  3. packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts

    • Updated import from UninstallOperatorOverlay to useUninstallOperatorModal
    • Renamed launcherlaunchModal (migration guide compliance)
    • Changed from direct overlay invocation to hook pattern
    • Bug Fix: Memoized props object to prevent infinite render loop
    • Updated dependency array to include uninstallOperatorModal

Technical Implementation

Pattern Consolidation

The modal had four conflicting patterns in the partially migrated state:

  1. ❌ Old createModalLauncher export (unused)
  2. ❌ Incorrect UninstallOperatorOverlay (manual Backdrop + Modal)
  3. ✅ Correct UninstallOperatorModalProvider (used ModalWrapper)
  4. ⚠️ Custom hook useUninstallOperatorModal (non-standard but working)

Migrated to:

  • Single correct overlay pattern with ModalWrapper
  • Proper naming conventions (*Overlay suffix)
  • Hook-based integration for consumers
  • Lazy loading for code splitting

This pattern:

  • ✅ Maintains backward compatibility with existing consumers
  • ✅ Provides a convenient API for pre-binding props
  • ✅ Follows React hooks conventions
  • ✅ Used by 2 of 3 consumers (already correct)

Consumers

Updated:

  • useOperatorActions.ts - Changed from incorrect direct overlay to hook pattern

Verified (no changes needed):

  • useSubscriptionActions.ts - Already using hook pattern correctly
  • subscription.tsx - Already using hook pattern correctly

Bug Fixes

1. Infinite Render Loop

Problem:
The props object passed to useUninstallOperatorModal was created fresh on every render, causing useCallback to return a new function each time, triggering useMemo to recompute infinitely.

Error:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect

Solution:
Memoized the props object to only change when dependencies change:

File: useOperatorActions.ts:19-31

2. Network Error Handling

Problem:
When the network is offline, the error object doesn't have a response property, causing err.response.status to crash with "Cannot read properties of undefined (reading 'status')".

Error:

TypeError: Cannot read properties of undefined (reading 'status')
    at clusterServiceVersionExists (uninstall-operator-modal.tsx:146:26)

Solution:
Added optional chaining to safely access the response status:

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Enhanced error handling during resource and operator deletion to prevent application crashes.
  • Refactor

    • Improved navigation reliability after completing deletion and uninstall operations.
    • Optimized performance of modal components through lazy-loading enhancements.
    • Modernized internal modal architecture for better maintainability and stability.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 11, 2026

@sg00dwin: No Jira issue with key MCONSOLE-5049 exists in the tracker at https://issues.redhat.com/.
Once a valid jira issue is referenced in the title of this pull request, request a refresh with /jira refresh.

Details

In response to this:

This pull request migrates two OLM modals from the legacy createModalLauncher pattern and React Router v5 history.push() to the modern OverlayComponent pattern with useNavigate(). This migration is part of CONSOLE-5012 (modal migration) and removes blockers for the React Router v7 upgrade (CONSOLE-5049).

Changes Made

Modals Migrated (2 total)

1. DeleteResourceModal - Deletes operator-managed resources
2. UninstallOperatorModal - Uninstalls operators and their operands from the cluster

Files Modified

DeleteResourceModal:

  1. packages/operator-lifecycle-manager/src/components/modals/DeleteResourceModal.tsx
  • Removed createModalLauncher import and export
  • Added useOverlay import
  • Exported component as DeleteResourceOverlay with OverlayComponent type
  • Wrapped modal in ModalWrapper with proper prop spreading
  • Added lazy-loaded export to modals/index.ts

UninstallOperatorModal:

  1. packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx
  • Removed createModalLauncher import and export
  • Removed React Router v5 history import
  • Added React Router v6 compat useNavigate import
  • Removed incorrect UninstallOperatorOverlay implementation (manual Backdrop + Modal)
  • Renamed UninstallOperatorModalProviderUninstallOperatorModalOverlay
  • Exported overlay component with export const pattern
  • Updated useUninstallOperatorModal hook to use launchModal (not launcher)
  • Removed unused blocking prop from type definition
  • Migrated from history.push() to navigate() in closeAndRedirect callback
  • Bug Fix: Added optional chaining to err.response?.status for network error handling
  1. packages/operator-lifecycle-manager/src/components/modals/index.ts (created)
  • Added lazy-loaded exports for both modals:
    • LazyDeleteResourceOverlay
    • LazyUninstallOperatorModalOverlay
  • Implemented code splitting with webpack chunk names
  1. packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts
  • Updated import from UninstallOperatorOverlay to useUninstallOperatorModal
  • Renamed launcherlaunchModal (migration guide compliance)
  • Changed from direct overlay invocation to hook pattern
  • Bug Fix: Memoized props object to prevent infinite render loop
  • Updated dependency array to include uninstallOperatorModal

Technical Implementation

Pattern Consolidation

The modal had four conflicting patterns in the partially migrated state:

  1. ❌ Old createModalLauncher export (unused)
  2. ❌ Incorrect UninstallOperatorOverlay (manual Backdrop + Modal)
  3. ✅ Correct UninstallOperatorModalProvider (used ModalWrapper)
  4. ⚠️ Custom hook useUninstallOperatorModal (non-standard but working)

Migrated to:

  • Single correct overlay pattern with ModalWrapper
  • Proper naming conventions (*Overlay suffix)
  • Hook-based integration for consumers
  • Lazy loading for code splitting

Naming Convention

All components and exports follow the *Overlay naming pattern:

  • Component: UninstallOperatorModalOverlay
  • Type: UninstallOperatorModalOverlayProps
  • Hook variable: launchModal (not launcher)
  • Lazy export: LazyUninstallOperatorModalOverlay

Custom Hook Pattern

The migration preserves the existing useUninstallOperatorModal custom hook for backward compatibility:

export const useUninstallOperatorModal = (props: UninstallOperatorModalProps) => {
 const launchModal = useOverlay();
 return useCallback(
   () => launchModal<UninstallOperatorModalOverlayProps>(UninstallOperatorModalOverlay, props),
   [launchModal, props],
 );
};

This pattern:

  • ✅ Maintains backward compatibility with existing consumers
  • ✅ Provides a convenient API for pre-binding props
  • ✅ Follows React hooks conventions
  • ✅ Used by 2 of 3 consumers (already correct)

Consumers

Updated:

  • useOperatorActions.ts - Changed from incorrect direct overlay to hook pattern

Verified (no changes needed):

  • useSubscriptionActions.ts - Already using hook pattern correctly
  • subscription.tsx - Already using hook pattern correctly

Bug Fixes

1. Infinite Render Loop

Problem:
The props object passed to useUninstallOperatorModal was created fresh on every render, causing useCallback to return a new function each time, triggering useMemo to recompute infinitely.

Error:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect

Solution:
Memoized the props object to only change when dependencies change:

File: useOperatorActions.ts:19-31

2. Network Error Handling

Problem:
When the network is offline, the error object doesn't have a response property, causing err.response.status to crash with "Cannot read properties of undefined (reading 'status')".

Error:

TypeError: Cannot read properties of undefined (reading 'status')
   at clusterServiceVersionExists (uninstall-operator-modal.tsx:146:26)

Solution:
Added optional chaining to safely access the response status:

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.

1 similar comment
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 11, 2026

@sg00dwin: No Jira issue with key MCONSOLE-5049 exists in the tracker at https://issues.redhat.com/.
Once a valid jira issue is referenced in the title of this pull request, request a refresh with /jira refresh.

Details

In response to this:

This pull request migrates two OLM modals from the legacy createModalLauncher pattern and React Router v5 history.push() to the modern OverlayComponent pattern with useNavigate(). This migration is part of CONSOLE-5012 (modal migration) and removes blockers for the React Router v7 upgrade (CONSOLE-5049).

Changes Made

Modals Migrated (2 total)

1. DeleteResourceModal - Deletes operator-managed resources
2. UninstallOperatorModal - Uninstalls operators and their operands from the cluster

Files Modified

DeleteResourceModal:

  1. packages/operator-lifecycle-manager/src/components/modals/DeleteResourceModal.tsx
  • Removed createModalLauncher import and export
  • Added useOverlay import
  • Exported component as DeleteResourceOverlay with OverlayComponent type
  • Wrapped modal in ModalWrapper with proper prop spreading
  • Added lazy-loaded export to modals/index.ts

UninstallOperatorModal:

  1. packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx
  • Removed createModalLauncher import and export
  • Removed React Router v5 history import
  • Added React Router v6 compat useNavigate import
  • Removed incorrect UninstallOperatorOverlay implementation (manual Backdrop + Modal)
  • Renamed UninstallOperatorModalProviderUninstallOperatorModalOverlay
  • Exported overlay component with export const pattern
  • Updated useUninstallOperatorModal hook to use launchModal (not launcher)
  • Removed unused blocking prop from type definition
  • Migrated from history.push() to navigate() in closeAndRedirect callback
  • Bug Fix: Added optional chaining to err.response?.status for network error handling
  1. packages/operator-lifecycle-manager/src/components/modals/index.ts (created)
  • Added lazy-loaded exports for both modals:
    • LazyDeleteResourceOverlay
    • LazyUninstallOperatorModalOverlay
  • Implemented code splitting with webpack chunk names
  1. packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts
  • Updated import from UninstallOperatorOverlay to useUninstallOperatorModal
  • Renamed launcherlaunchModal (migration guide compliance)
  • Changed from direct overlay invocation to hook pattern
  • Bug Fix: Memoized props object to prevent infinite render loop
  • Updated dependency array to include uninstallOperatorModal

Technical Implementation

Pattern Consolidation

The modal had four conflicting patterns in the partially migrated state:

  1. ❌ Old createModalLauncher export (unused)
  2. ❌ Incorrect UninstallOperatorOverlay (manual Backdrop + Modal)
  3. ✅ Correct UninstallOperatorModalProvider (used ModalWrapper)
  4. ⚠️ Custom hook useUninstallOperatorModal (non-standard but working)

Migrated to:

  • Single correct overlay pattern with ModalWrapper
  • Proper naming conventions (*Overlay suffix)
  • Hook-based integration for consumers
  • Lazy loading for code splitting

Naming Convention

All components and exports follow the *Overlay naming pattern:

  • Component: UninstallOperatorModalOverlay
  • Type: UninstallOperatorModalOverlayProps
  • Hook variable: launchModal (not launcher)
  • Lazy export: LazyUninstallOperatorModalOverlay

Custom Hook Pattern

The migration preserves the existing useUninstallOperatorModal custom hook for backward compatibility:

export const useUninstallOperatorModal = (props: UninstallOperatorModalProps) => {
 const launchModal = useOverlay();
 return useCallback(
   () => launchModal<UninstallOperatorModalOverlayProps>(UninstallOperatorModalOverlay, props),
   [launchModal, props],
 );
};

This pattern:

  • ✅ Maintains backward compatibility with existing consumers
  • ✅ Provides a convenient API for pre-binding props
  • ✅ Follows React hooks conventions
  • ✅ Used by 2 of 3 consumers (already correct)

Consumers

Updated:

  • useOperatorActions.ts - Changed from incorrect direct overlay to hook pattern

Verified (no changes needed):

  • useSubscriptionActions.ts - Already using hook pattern correctly
  • subscription.tsx - Already using hook pattern correctly

Bug Fixes

1. Infinite Render Loop

Problem:
The props object passed to useUninstallOperatorModal was created fresh on every render, causing useCallback to return a new function each time, triggering useMemo to recompute infinitely.

Error:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect

Solution:
Memoized the props object to only change when dependencies change:

File: useOperatorActions.ts:19-31

2. Network Error Handling

Problem:
When the network is offline, the error object doesn't have a response property, causing err.response.status to crash with "Cannot read properties of undefined (reading 'status')".

Error:

TypeError: Cannot read properties of undefined (reading 'status')
   at clusterServiceVersionExists (uninstall-operator-modal.tsx:146:26)

Solution:
Added optional chaining to safely access the response status:

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 openshift-ci bot added the component/dev-console Related to dev-console label Feb 11, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 11, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: sg00dwin
Once this PR has been reviewed and has the lgtm label, please assign vojtechszocs for approval. For more information see the Code Review Process.

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

Details 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 openshift-ci bot added component/helm Related to helm-plugin component/olm Related to OLM component/shared Related to console-shared labels Feb 11, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 11, 2026

@sg00dwin: No Jira issue with key MCONSOLE-5049 exists in the tracker at https://issues.redhat.com/.
Once a valid jira issue is referenced in the title of this pull request, request a refresh with /jira refresh.

Details

In response to this:

This pull request migrates two OLM modals from the legacy createModalLauncher pattern and React Router v5 history.push() to the modern OverlayComponent pattern with useNavigate(). This migration is part of CONSOLE-5012 (modal migration) and removes blockers for the React Router v7 upgrade (CONSOLE-5049).

Changes Made

Modals Migrated (2 total)

1. DeleteResourceModal - Deletes operator-managed resources
2. UninstallOperatorModal - Uninstalls operators and their operands from the cluster

Files Modified

DeleteResourceModal:

  1. packages/operator-lifecycle-manager/src/components/modals/DeleteResourceModal.tsx
  • Removed createModalLauncher import and export
  • Added useOverlay import
  • Exported component as DeleteResourceOverlay with OverlayComponent type
  • Wrapped modal in ModalWrapper with proper prop spreading
  • Added lazy-loaded export to modals/index.ts

UninstallOperatorModal:

  1. packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx
  • Removed createModalLauncher import and export
  • Removed React Router v5 history import
  • Added React Router v6 compat useNavigate import
  • Removed incorrect UninstallOperatorOverlay implementation (manual Backdrop + Modal)
  • Renamed UninstallOperatorModalProviderUninstallOperatorModalOverlay
  • Exported overlay component with export const pattern
  • Updated useUninstallOperatorModal hook to use launchModal (not launcher)
  • Removed unused blocking prop from type definition
  • Migrated from history.push() to navigate() in closeAndRedirect callback
  • Bug Fix: Added optional chaining to err.response?.status for network error handling
  1. packages/operator-lifecycle-manager/src/components/modals/index.ts (created)
  • Added lazy-loaded exports for both modals:
    • LazyDeleteResourceOverlay
    • LazyUninstallOperatorModalOverlay
  • Implemented code splitting with webpack chunk names
  1. packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts
  • Updated import from UninstallOperatorOverlay to useUninstallOperatorModal
  • Renamed launcherlaunchModal (migration guide compliance)
  • Changed from direct overlay invocation to hook pattern
  • Bug Fix: Memoized props object to prevent infinite render loop
  • Updated dependency array to include uninstallOperatorModal

Technical Implementation

Pattern Consolidation

The modal had four conflicting patterns in the partially migrated state:

  1. ❌ Old createModalLauncher export (unused)
  2. ❌ Incorrect UninstallOperatorOverlay (manual Backdrop + Modal)
  3. ✅ Correct UninstallOperatorModalProvider (used ModalWrapper)
  4. ⚠️ Custom hook useUninstallOperatorModal (non-standard but working)

Migrated to:

  • Single correct overlay pattern with ModalWrapper
  • Proper naming conventions (*Overlay suffix)
  • Hook-based integration for consumers
  • Lazy loading for code splitting

Naming Convention

All components and exports follow the *Overlay naming pattern:

  • Component: UninstallOperatorModalOverlay
  • Type: UninstallOperatorModalOverlayProps
  • Hook variable: launchModal (not launcher)
  • Lazy export: LazyUninstallOperatorModalOverlay

Custom Hook Pattern

The migration preserves the existing useUninstallOperatorModal custom hook for backward compatibility:

export const useUninstallOperatorModal = (props: UninstallOperatorModalProps) => {
 const launchModal = useOverlay();
 return useCallback(
   () => launchModal<UninstallOperatorModalOverlayProps>(UninstallOperatorModalOverlay, props),
   [launchModal, props],
 );
};

This pattern:

  • ✅ Maintains backward compatibility with existing consumers
  • ✅ Provides a convenient API for pre-binding props
  • ✅ Follows React hooks conventions
  • ✅ Used by 2 of 3 consumers (already correct)

Consumers

Updated:

  • useOperatorActions.ts - Changed from incorrect direct overlay to hook pattern

Verified (no changes needed):

  • useSubscriptionActions.ts - Already using hook pattern correctly
  • subscription.tsx - Already using hook pattern correctly

Bug Fixes

1. Infinite Render Loop

Problem:
The props object passed to useUninstallOperatorModal was created fresh on every render, causing useCallback to return a new function each time, triggering useMemo to recompute infinitely.

Error:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect

Solution:
Memoized the props object to only change when dependencies change:

File: useOperatorActions.ts:19-31

2. Network Error Handling

Problem:
When the network is offline, the error object doesn't have a response property, causing err.response.status to crash with "Cannot read properties of undefined (reading 'status')".

Error:

TypeError: Cannot read properties of undefined (reading 'status')
   at clusterServiceVersionExists (uninstall-operator-modal.tsx:146:26)

Solution:
Added optional chaining to safely access the response status:

Summary by CodeRabbit

Release Notes

  • Bug Fixes

  • Enhanced error handling during resource and operator deletion to prevent application crashes.

  • Refactor

  • Improved navigation reliability after completing deletion and uninstall operations.

  • Optimized performance of modal components through lazy-loading enhancements.

  • Modernized internal modal architecture for better maintainability and stability.

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
Contributor

coderabbitai bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

The PR migrates modal patterns across multiple frontend packages from a launcher-based approach to an overlay component architecture. DeleteResourceModal, UninstallOperatorModal, and related action creators are refactored to use OverlayComponent-based wrappers and React hooks. Modal launchers are replaced with lazy-loaded overlay components. Navigation is updated to use route-based navigation instead of history manipulation. Action creators (DeleteApplicationAction, getHelmDeleteAction) are converted to hook-based patterns. The redirect field in HelmActionsScope changes from boolean to string. Modal props are simplified and aligned with the new overlay-driven flow.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: migrating DeleteResourceModal to the useOverlay pattern, which is the primary objective and the most significant architectural shift in this changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In
`@frontend/packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx`:
- Around line 650-656: The hook useUninstallOperatorModal currently depends on
the entire props object which forces callers to memoize; change it to
destructure the specific fields from UninstallOperatorModalProps and include
those individual fields (e.g., kind, resource, onClose, etc.) in the useCallback
dependency array instead of the whole props object, or switch to the established
pattern used by useRemoveModalLauncher that takes launcher and explicit prop
values (depend on [launchModal, /* individual props */]) and pass the
reconstructed props into UninstallOperatorModalOverlay when calling launchModal;
update the signature of useUninstallOperatorModal accordingly so callers need
not memoize an inline object.
🧹 Nitpick comments (3)
frontend/packages/helm-plugin/src/actions/creators.ts (1)

1-49: Encode Helm release params before building the delete URL.

Release names/namespaces can include characters that should be URL-encoded; encoding avoids edge-case request failures and keeps the query string well-formed.

♻️ Proposed hardening
-            return coFetchJSON.delete(
-              `/api/helm/release/async?name=${releaseName}&ns=${namespace}&version=${releaseVersion}`,
-              null,
-              null,
-              -1,
-            );
+            return coFetchJSON.delete(
+              `/api/helm/release/async?name=${encodeURIComponent(
+                releaseName,
+              )}&ns=${encodeURIComponent(namespace)}&version=${encodeURIComponent(
+                String(releaseVersion),
+              )}`,
+              null,
+              null,
+              -1,
+            );
frontend/packages/console-shared/src/components/modals/DeleteResourceModal.tsx (1)

98-113: Overlay wrapper is functional but verbose.

The explicit prop passing works correctly. The blocking prop on ModalWrapper is appropriate for a destructive delete operation—preventing accidental dismissal via overlay click.

Consider simplifying with a spread operator for maintainability:

♻️ Optional: Reduce verbosity with spread
 export const DeleteResourceModalOverlay: OverlayComponent<DeleteResourceModalProps> = (props) => {
+  const { closeOverlay, ...rest } = props;
   return (
-    <ModalWrapper blocking onClose={props.closeOverlay}>
-      <DeleteResourceModal
-        close={props.closeOverlay}
-        cancel={props.closeOverlay}
-        resourceName={props.resourceName}
-        resourceType={props.resourceType}
-        actionLabel={props.actionLabel}
-        actionLabelKey={props.actionLabelKey}
-        redirect={props.redirect}
-        onSubmit={props.onSubmit}
-      />
+    <ModalWrapper blocking onClose={closeOverlay}>
+      <DeleteResourceModal close={closeOverlay} cancel={closeOverlay} {...rest} />
     </ModalWrapper>
   );
 };
frontend/packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx (1)

660-672: Remove unused type props to reduce API surface.

k8sGet and k8sPatch are defined in UninstallOperatorModalProps but never used. The component destructures only cancel, close, csv, k8sKill, and subscription (line 59). Instead, k8sPatch is imported directly from @console/internal/module/k8s (line 37) and used at line 171, while the component relies on k8sGetResource from the SDK (line 10). Removing these props cleans up the type contract and eliminates confusion about the actual API surface.

🧹 Clean up unused type props
 export type UninstallOperatorModalProps = {
   cancel?: () => void;
   close?: () => void;
   k8sKill: (kind: K8sKind, resource: K8sResourceKind, options: any, json: any) => Promise<any>;
-  k8sGet: (kind: K8sKind, name: string, namespace: string) => Promise<K8sResourceKind>;
-  k8sPatch: (
-    kind: K8sKind,
-    resource: K8sResourceKind,
-    data: { op: string; path: string; value: any }[],
-  ) => Promise<any>;
   subscription: K8sResourceKind;
   csv?: K8sResourceKind;
 };

Comment on lines 650 to 656
export const useUninstallOperatorModal = (props: UninstallOperatorModalProps) => {
const launcher = useOverlay();
const launchModal = useOverlay();
return useCallback(
() => launcher<UninstallOperatorModalProviderProps>(UninstallOperatorModalProvider, props),
[launcher, props],
() => launchModal<UninstallOperatorModalOverlayProps>(UninstallOperatorModalOverlay, props),
[launchModal, props],
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all useOverlay-based hooks to understand the pattern
rg -n -B2 -A10 'useOverlay\(\)' --type=ts --type=tsx | head -100

Repository: openshift/console

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Check the specific hook and its usage
rg -n -B5 -A15 'export const useUninstallOperatorModal' --type=ts --type=tsx

Repository: openshift/console

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Search for how useUninstallOperatorModal is called in the codebase
rg -n 'useUninstallOperatorModal' --type=ts --type=tsx -B2 -A2 | head -80

Repository: openshift/console

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Check useOperatorActions.ts to see if memoization was added
rg -n -B5 -A20 'useUninstallOperatorModal\|useOperatorActions' --type=ts --type=tsx | grep -A20 'useOperatorActions' | head -60

Repository: openshift/console

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Check UninstallOperatorModalProps type to understand what props are
rg -n 'interface UninstallOperatorModalProps\|type UninstallOperatorModalProps' --type=ts --type=tsx -A8

Repository: openshift/console

Length of output: 88


🏁 Script executed:

#!/bin/bash
# Find all useOverlay-based hooks (search without type restriction)
rg -n -B2 -A10 'useOverlay\(\)' | head -120

Repository: openshift/console

Length of output: 10016


🏁 Script executed:

#!/bin/bash
# Check the specific hook location and implementation
cat -n frontend/packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx | sed -n '640,670p'

Repository: openshift/console

Length of output: 1402


🏁 Script executed:

#!/bin/bash
# Search for UninstallOperatorModalProps definition
rg -n 'interface UninstallOperatorModalProps|type UninstallOperatorModalProps' -A8

Repository: openshift/console

Length of output: 1351


🏁 Script executed:

#!/bin/bash
# Search for how useUninstallOperatorModal is used
rg -n 'useUninstallOperatorModal' -B3 -A3

Repository: openshift/console

Length of output: 6776


🏁 Script executed:

#!/bin/bash
# Check if useOperatorActions.ts uses memoization
rg -n 'useOperatorActions\|useUninstallOperatorModal' -B5 -A15 frontend/packages/operator-lifecycle-manager/src/

Repository: openshift/console

Length of output: 43


Dependency on props object in useCallback is a footgun for callers.

This hook includes props in the dependency array, requiring callers to memoize. While useOperatorActions.ts correctly memoizes uninstallOperatorModalProps, both useSubscriptionActions.ts (line 34) and subscription.tsx (line 431) pass inline objects, causing unnecessary re-renders and callback churn on every render.

Other similar modal hooks across the codebase (useDeleteNamespaceModalLauncher, useErrorModalLauncher, useAddSecretToWorkloadModal) follow the same risky pattern. A safer alternative exists in useRemoveModalLauncher, which destructures and depends on individual properties ([launcher, kind, resource, volume]) rather than object identity.

Either document this requirement prominently or restructure to depend on individual props rather than object identity. The latter is safer for maintainability since it eliminates the implicit contract that callers must memoize.

🤖 Prompt for AI Agents
In
`@frontend/packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx`
around lines 650 - 656, The hook useUninstallOperatorModal currently depends on
the entire props object which forces callers to memoize; change it to
destructure the specific fields from UninstallOperatorModalProps and include
those individual fields (e.g., kind, resource, onClose, etc.) in the useCallback
dependency array instead of the whole props object, or switch to the established
pattern used by useRemoveModalLauncher that takes launcher and explicit prop
values (depend on [launchModal, /* individual props */]) and pass the
reconstructed props into UninstallOperatorModalOverlay when calling launchModal;
update the signature of useUninstallOperatorModal accordingly so callers need
not memoize an inline object.

@rhamilto rhamilto changed the title MCONSOLE-5049: Migrate UninstallOperatorModal and DeleteResourceModal to useOverlay pattern CONSOLE-5049: Migrate UninstallOperatorModal and DeleteResourceModal to useOverlay pattern Feb 11, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Feb 11, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 11, 2026

@sg00dwin: This pull request references CONSOLE-5049 which is a valid jira issue.

Details

In response to this:

This pull request migrates two OLM modals from the legacy createModalLauncher pattern and React Router v5 history.push() to the modern OverlayComponent pattern with useNavigate(). This migration is part of CONSOLE-5012 (modal migration) and removes blockers for the React Router v7 upgrade (CONSOLE-5049).

Changes Made

Modals Migrated (2 total)

1. DeleteResourceModal - Deletes operator-managed resources
2. UninstallOperatorModal - Uninstalls operators and their operands from the cluster

Files Modified

DeleteResourceModal:

  1. packages/operator-lifecycle-manager/src/components/modals/DeleteResourceModal.tsx
  • Removed createModalLauncher import and export
  • Added useOverlay import
  • Exported component as DeleteResourceOverlay with OverlayComponent type
  • Wrapped modal in ModalWrapper with proper prop spreading
  • Added lazy-loaded export to modals/index.ts

UninstallOperatorModal:

  1. packages/operator-lifecycle-manager/src/components/modals/uninstall-operator-modal.tsx
  • Removed createModalLauncher import and export
  • Removed React Router v5 history import
  • Added React Router v6 compat useNavigate import
  • Removed incorrect UninstallOperatorOverlay implementation (manual Backdrop + Modal)
  • Renamed UninstallOperatorModalProviderUninstallOperatorModalOverlay
  • Exported overlay component with export const pattern
  • Updated useUninstallOperatorModal hook to use launchModal (not launcher)
  • Removed unused blocking prop from type definition
  • Migrated from history.push() to navigate() in closeAndRedirect callback
  • Bug Fix: Added optional chaining to err.response?.status for network error handling
  1. packages/operator-lifecycle-manager/src/components/modals/index.ts (created)
  • Added lazy-loaded exports for both modals:
    • LazyDeleteResourceOverlay
    • LazyUninstallOperatorModalOverlay
  • Implemented code splitting with webpack chunk names
  1. packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts
  • Updated import from UninstallOperatorOverlay to useUninstallOperatorModal
  • Renamed launcherlaunchModal (migration guide compliance)
  • Changed from direct overlay invocation to hook pattern
  • Bug Fix: Memoized props object to prevent infinite render loop
  • Updated dependency array to include uninstallOperatorModal

Technical Implementation

Pattern Consolidation

The modal had four conflicting patterns in the partially migrated state:

  1. ❌ Old createModalLauncher export (unused)
  2. ❌ Incorrect UninstallOperatorOverlay (manual Backdrop + Modal)
  3. ✅ Correct UninstallOperatorModalProvider (used ModalWrapper)
  4. ⚠️ Custom hook useUninstallOperatorModal (non-standard but working)

Migrated to:

  • Single correct overlay pattern with ModalWrapper
  • Proper naming conventions (*Overlay suffix)
  • Hook-based integration for consumers
  • Lazy loading for code splitting

Naming Convention

All components and exports follow the *Overlay naming pattern:

  • Component: UninstallOperatorModalOverlay
  • Type: UninstallOperatorModalOverlayProps
  • Hook variable: launchModal (not launcher)
  • Lazy export: LazyUninstallOperatorModalOverlay

Custom Hook Pattern

The migration preserves the existing useUninstallOperatorModal custom hook for backward compatibility:

export const useUninstallOperatorModal = (props: UninstallOperatorModalProps) => {
 const launchModal = useOverlay();
 return useCallback(
   () => launchModal<UninstallOperatorModalOverlayProps>(UninstallOperatorModalOverlay, props),
   [launchModal, props],
 );
};

This pattern:

  • ✅ Maintains backward compatibility with existing consumers
  • ✅ Provides a convenient API for pre-binding props
  • ✅ Follows React hooks conventions
  • ✅ Used by 2 of 3 consumers (already correct)

Consumers

Updated:

  • useOperatorActions.ts - Changed from incorrect direct overlay to hook pattern

Verified (no changes needed):

  • useSubscriptionActions.ts - Already using hook pattern correctly
  • subscription.tsx - Already using hook pattern correctly

Bug Fixes

1. Infinite Render Loop

Problem:
The props object passed to useUninstallOperatorModal was created fresh on every render, causing useCallback to return a new function each time, triggering useMemo to recompute infinitely.

Error:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect

Solution:
Memoized the props object to only change when dependencies change:

File: useOperatorActions.ts:19-31

2. Network Error Handling

Problem:
When the network is offline, the error object doesn't have a response property, causing err.response.status to crash with "Cannot read properties of undefined (reading 'status')".

Error:

TypeError: Cannot read properties of undefined (reading 'status')
   at clusterServiceVersionExists (uninstall-operator-modal.tsx:146:26)

Solution:
Added optional chaining to safely access the response status:

Summary by CodeRabbit

Release Notes

  • Bug Fixes

  • Enhanced error handling during resource and operator deletion to prevent application crashes.

  • Refactor

  • Improved navigation reliability after completing deletion and uninstall operations.

  • Optimized performance of modal components through lazy-loading enhancements.

  • Modernized internal modal architecture for better maintainability and stability.

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.

subscription,
csv: resource,
}),
[subscription, resource],
Copy link
Member

Choose a reason for hiding this comment

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

Missing props in the dependency array

Copy link
Member Author

Choose a reason for hiding this comment

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

eslint is saying these are unnecessary dependencies since they are outer scope values (module imports) that don't cause re-renders

yarn eslint frontend/packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts

28:5  error  React Hook useMemo has unnecessary dependencies: 'k8sGet', 'k8sKill', and 'k8sPatch'. Either exclude them or remove the dependency array. Outer scope values like 'k8sKill' aren't valid dependencies because mutating them doesn't re-render the component

Copy link
Member

@rhamilto rhamilto left a comment

Choose a reason for hiding this comment

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

uninstall-operator-modal.tsx is also changed in #15968. We should just put all OLM changes there?

…er v6

Update DeleteResourceModal, dev-console actions, and helm-plugin actions from history.push()
to useNavigate() for React Router v7 compatibility.

Assisted by: Claude Code
@sg00dwin sg00dwin force-pushed the CONSOLE-5049-router-dependent-modals branch from b34ed9f to 234ba09 Compare February 11, 2026 23:17
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 12, 2026

@sg00dwin: all tests passed!

Full PR test history. Your PR dashboard.

Details

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.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 12, 2026

@sg00dwin: This pull request references CONSOLE-5049 which is a valid jira issue.

Details

In response to this:

This pull request migrates two OLM modals from the legacy createModalLauncher pattern and React Router v5 history.push() to the modern OverlayComponent pattern with useNavigate(). This migration is part of CONSOLE-5012 (modal migration) and removes blockers for the React Router v7 upgrade (CONSOLE-5049).

Changes Made

Modals Migrated (2 total)

1. DeleteResourceModal - Deletes operator-managed resources

Files Modified

DeleteResourceModal:

  1. packages/operator-lifecycle-manager/src/components/modals/DeleteResourceModal.tsx
  • Removed createModalLauncher import and export
  • Added useOverlay import
  • Exported component as DeleteResourceOverlay with OverlayComponent type
  • Wrapped modal in ModalWrapper with proper prop spreading
  • Added lazy-loaded export to modals/index.ts
  1. packages/operator-lifecycle-manager/src/components/modals/index.ts (created)
  • Added lazy-loaded exports for both modals:
    • LazyDeleteResourceOverlay
    • LazyUninstallOperatorModalOverlay
  • Implemented code splitting with webpack chunk names
  1. packages/operator-lifecycle-manager/src/actions/useOperatorActions.ts
  • Updated import from UninstallOperatorOverlay to useUninstallOperatorModal
  • Renamed launcherlaunchModal (migration guide compliance)
  • Changed from direct overlay invocation to hook pattern
  • Bug Fix: Memoized props object to prevent infinite render loop
  • Updated dependency array to include uninstallOperatorModal

Technical Implementation

Pattern Consolidation

The modal had four conflicting patterns in the partially migrated state:

  1. ❌ Old createModalLauncher export (unused)
  2. ❌ Incorrect UninstallOperatorOverlay (manual Backdrop + Modal)
  3. ✅ Correct UninstallOperatorModalProvider (used ModalWrapper)
  4. ⚠️ Custom hook useUninstallOperatorModal (non-standard but working)

Migrated to:

  • Single correct overlay pattern with ModalWrapper
  • Proper naming conventions (*Overlay suffix)
  • Hook-based integration for consumers
  • Lazy loading for code splitting

This pattern:

  • ✅ Maintains backward compatibility with existing consumers
  • ✅ Provides a convenient API for pre-binding props
  • ✅ Follows React hooks conventions
  • ✅ Used by 2 of 3 consumers (already correct)

Consumers

Updated:

  • useOperatorActions.ts - Changed from incorrect direct overlay to hook pattern

Verified (no changes needed):

  • useSubscriptionActions.ts - Already using hook pattern correctly
  • subscription.tsx - Already using hook pattern correctly

Bug Fixes

1. Infinite Render Loop

Problem:
The props object passed to useUninstallOperatorModal was created fresh on every render, causing useCallback to return a new function each time, triggering useMemo to recompute infinitely.

Error:

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect

Solution:
Memoized the props object to only change when dependencies change:

File: useOperatorActions.ts:19-31

2. Network Error Handling

Problem:
When the network is offline, the error object doesn't have a response property, causing err.response.status to crash with "Cannot read properties of undefined (reading 'status')".

Error:

TypeError: Cannot read properties of undefined (reading 'status')
   at clusterServiceVersionExists (uninstall-operator-modal.tsx:146:26)

Solution:
Added optional chaining to safely access the response status:

Summary by CodeRabbit

Release Notes

  • Bug Fixes

  • Enhanced error handling during resource and operator deletion to prevent application crashes.

  • Refactor

  • Improved navigation reliability after completing deletion and uninstall operations.

  • Optimized performance of modal components through lazy-loading enhancements.

  • Modernized internal modal architecture for better maintainability and stability.

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.

@sg00dwin sg00dwin changed the title CONSOLE-5049: Migrate UninstallOperatorModal and DeleteResourceModal to useOverlay pattern CONSOLE-5049: Migrate DeleteResourceModal to useOverlay pattern Feb 12, 2026
@sg00dwin
Copy link
Member Author

/assign @yanpzhan

Tech debt, so assigning labels
/label px-approved
/label docs-approved

@openshift-ci openshift-ci bot added px-approved Signifies that Product Support has signed off on this PR docs-approved Signifies that Docs has signed off on this PR labels Feb 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/dev-console Related to dev-console component/helm Related to helm-plugin component/olm Related to OLM component/shared Related to console-shared docs-approved Signifies that Docs has signed off on this PR jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. px-approved Signifies that Product Support has signed off on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants