Skip to content

Conversation

@rhamilto
Copy link
Member

@rhamilto rhamilto commented Jan 20, 2026

Summary

Migrates delete modals and PVC-related modals from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern. This is part of CONSOLE-5012 to modernize modal handling across the console.

Changes

Modals Migrated (5 total)

All modals now follow the OverlayComponent pattern with consistent implementation:

  1. DeleteModal (public/components/modals/delete-modal.tsx)

    • Wrapped with DeleteModalProvider: OverlayComponent
    • Exported as LazyDeleteModalProvider with lazy loading
  2. DeletePVCModal (public/components/modals/delete-pvc-modal.tsx)

    • Wrapped with DeletePVCModalProvider: OverlayComponent
    • Includes PVC-specific deletion logic
  3. ExpandPVCModal (public/components/modals/expand-pvc-modal.tsx)

    • Wrapped with ExpandPVCModalProvider: OverlayComponent
    • Exported as LazyExpandPVCModalProvider
  4. ClonePVCModal (packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx)

    • Wrapped with ClonePVCModalProvider: OverlayComponent
    • Exported as LazyClonePVCModalProvider
  5. RestorePVCModal (packages/console-app/src/components/modals/restore-pvc/restore-pvc-modal.tsx)

    • Wrapped with RestorePVCModalProvider: OverlayComponent
    • Exported as LazyRestorePVCModalProvider

Action Creators Converted to Hooks (3 total)

To integrate with the React hooks system and useOverlay() pattern:

  1. useDeleteResourceAction (packages/dev-console/src/actions/context-menu.ts)

    • Converted from DeleteResourceAction function to hook
    • Uses useOverlay() and LazyDeleteModalProvider
    • Updated consumers: deployment-provider.ts, deploymentconfig-provider.ts
  2. useDeleteApplicationAction (packages/dev-console/src/actions/context-menu.ts)

    • Converted from function to hook (existing)
    • Uses deleteResourceModal from @console/shared
  3. useDeleteKnativeServiceResource (packages/knative-plugin/src/actions/creators.ts)

    • Converted from deleteKnativeServiceResource function to hook
    • Conditional cleanUpWorkload based on serviceCreatedFromWebFlag
    • Updated consumer: knative-plugin/src/actions/providers.ts

React Hooks Dependency Fixes (Critical)

Fixed infinite loop issues caused by launchModal in dependency arrays:

  • useCommonActions.ts: Removed launchModal and launchCountModal from dependency array
  • usePVCActions.ts: Removed launchModal from dependency array
  • useVolumeSnapshotActions.ts: Removed launchModal from dependency array

Rationale: launchModal from useOverlay() maintains referential stability and doesn't need to be in dependency arrays. Including it causes "max depth exceeded" errors.

Infrastructure Updates

  • OverlayProvider.tsx: Added <Suspense> wrapper for lazy-loaded overlay components
  • modals/index.ts: Updated exports to use consistent React.lazy() pattern with all modals using .then((m) => ({ default: m.default }))

Pattern Example

// Modal file
export const DeleteModalProvider: OverlayComponent<DeleteModalProps> = (props) => {
  return (
    <ModalWrapper blocking onClose={props.closeOverlay}>
      <DeleteModal cancel={props.closeOverlay} close={props.closeOverlay} {...props} />
    </ModalWrapper>
  );
};

export default DeleteModalProvider;
// index.ts
export const LazyDeleteModalProvider = lazy(() =>
  import('./delete-modal').then((m) => ({ default: m.default })),
);
// Action hook
export const useDeleteResourceAction = (kind: K8sModel, obj: K8sResourceKind): Action => {
  const { t } = useTranslation();
  const launchModal = useOverlay();

  return useMemo(
    () => ({
      id: 'delete-resource',
      label: t('devconsole~Delete {{kind}}', { kind: kind.kind }),
      cta: () => launchModal(LazyDeleteModalProvider, { kind, resource: obj }),
      accessReview: asAccessReview(kind, obj, 'delete'),
    }),
    [t, kind, obj, launchModal], // launchModal is stable but included for completeness
  );
};

Benefits

  • ✅ Consistent pattern across all modals
  • ✅ Better code splitting with lazy loading
  • ✅ Eliminates infinite re-render bugs
  • ✅ Proper React hooks integration
  • ✅ Maintains backward compatibility with default exports
  • ✅ Improved maintainability

Testing

  • All 5 modals open and close correctly
  • Delete actions work for Deployments, DeploymentConfigs, and Knative Services
  • PVC actions work (expand, clone, restore, delete)
  • No console errors or infinite loops
  • Lazy loading works correctly (check Network tab for separate chunks)

Related

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jan 20, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 20, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

Migrates PVC-related modals (expand, clone, delete, restore) from the legacy createModalLauncher pattern to the modern OverlayComponent pattern with lazy loading.

Changes

  • Convert modal exports to OverlayComponent providers with ModalWrapper
  • Implement lazy loading for modal components using React's lazy()
  • Update action hooks (usePVCActions, useVolumeSnapshotActions) to use useOverlay() and launchModal()
  • Set react-modal app element globally in App component for accessibility
  • Maintain backward compatibility with existing modal exports

Test plan

  • Verify expand PVC modal opens and functions correctly
  • Verify clone PVC modal opens and functions correctly (when PVC is Bound)
  • Verify delete PVC modal opens and functions correctly
  • Verify restore PVC modal opens and functions correctly (from volume snapshot)
  • Verify modals close properly
  • Check that accessibility features work (aria attributes, focus management)

🤖 Generated with Claude Code

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 requested review from cajieh and jhadvig January 20, 2026 16:39
@openshift-ci openshift-ci bot added component/core Related to console core functionality approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Jan 20, 2026
@rhamilto rhamilto changed the title CONSOLE-5012: Migrate PVC modals to overlay pattern [WIP] CONSOLE-5012: Migrate PVC modals to overlay pattern Jan 20, 2026
@openshift-ci openshift-ci bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 20, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 20, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

Migrates PVC-related modals (expand, clone, delete, restore) from the legacy createModalLauncher pattern to the modern OverlayComponent pattern with lazy loading.

Changes

  • Convert modal exports to OverlayComponent providers with ModalWrapper
  • Implement lazy loading for modal components using React's lazy()
  • Update action hooks (usePVCActions, useVolumeSnapshotActions) to use useOverlay() and launchModal()
  • Set react-modal app element globally in App component for accessibility
  • Maintain backward compatibility with existing modal exports

Test plan

  • Verify expand PVC modal opens and functions correctly
  • Verify clone PVC modal opens and functions correctly (when PVC is Bound)
  • Verify delete PVC modal opens and functions correctly
  • Verify restore PVC modal opens and functions correctly (from volume snapshot)
  • Verify modals close properly
  • Check that accessibility features work (aria attributes, focus management)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
  • Updated modal infrastructure for PVC operations (expand, clone, delete) and volume snapshot restoration to use an overlay-based provider architecture.
  • Optimized modal loading performance through lazy-loading of modal providers.

✏️ Tip: You can customize this high-level summary in your review settings.

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

@coderabbitai
Copy link

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

The PR replaces direct modal launchers with Overlay-based provider components and lazy-loaded provider wrappers. Modal implementations (Clone, Expand, Delete, Restore, DeleteModal, etc.) now export OverlayComponent providers that wrap modals in ModalWrapper and wire closeOverlay. Action hooks and action creators were updated to call useOverlay/launchModal with Lazy*ModalProvider providers; several action factories were converted to hook forms (e.g., useDeleteResourceAction, useDeleteApplicationAction, useDeleteKnativeServiceResource). Modal.setAppElement is now initialized once in App via useEffect, per-call initialization removed, and OverlayProvider renders dynamic components inside Suspense. Some memo dependency arrays dropped launchModal with eslint-disable comments.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly reflects the main architectural refactoring: migration of delete and PVC modals from createModalLauncher to the OverlayComponent pattern with associated hook conversions.
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.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@frontend/public/components/modals/delete-pvc-modal.tsx`:
- Around line 9-16: Restore a default export so existing callers importing the
default don't break: export the modal component previously exposed as the
default (the DeletePVCModal component used with createModalLauncher) as the
file's default export in addition to any named exports; locate the
DeletePVCModal symbol (and the modal wrapper/props usage such as
ModalComponentProps) and add a default export for it so index
re-exports/back-compat continue to work.

In `@frontend/public/components/modals/index.ts`:
- Line 3: OverlayProvider is rendering lazy components from componentsMap
without a Suspense boundary causing runtime throws; import Suspense from 'react'
and wrap each mapped provider render (the map that returns <c.Component
{...c.props} closeOverlay={() => closeOverlay(id)} />) in <Suspense
fallback={null}> so each lazy modal provider is suspended safely; apply the same
change to the LazyClonePVCModalProvider and LazyRestorePVCModalProvider render
blocks so their lazy components are also wrapped with Suspense.
🧹 Nitpick comments (2)
frontend/packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx (1)

279-285: Provider implementation looks solid; consider explicit prop destructuring for clarity.

The pattern is correct: blocking prevents accidental dismissal during form entry, and mapping both cancel and close to closeOverlay aligns with how ClonePVCModal uses them (success path at line 132, cancel button at line 269).

One minor refinement: spreading ...props passes closeOverlay to the inner modal which doesn't consume it. While harmless, destructuring makes the prop threading more explicit:

✨ Optional: explicit prop destructuring
-export const ClonePVCModalProvider: OverlayComponent<ClonePVCModalProps> = (props) => {
+export const ClonePVCModalProvider: OverlayComponent<ClonePVCModalProps> = ({
+  closeOverlay,
+  ...modalProps
+}) => {
   return (
-    <ModalWrapper blocking onClose={props.closeOverlay}>
-      <ClonePVCModal cancel={props.closeOverlay} close={props.closeOverlay} {...props} />
+    <ModalWrapper blocking onClose={closeOverlay}>
+      <ClonePVCModal cancel={closeOverlay} close={closeOverlay} {...modalProps} />
     </ModalWrapper>
   );
 };
frontend/public/components/modals/expand-pvc-modal.tsx (1)

4-11: Keep close non‑optional to prevent runtime undefined calls.

Line 97 now inherits close?: () => void from ModalComponentProps, but Line 47 calls close() unguarded. Consider keeping close required in ExpandPVCModalProps (even if you still reuse the shared type) to avoid accidental misuse in tests or future direct renders.

♻️ Suggested type tightening
 export type ExpandPVCModalProps = {
   kind: K8sKind;
   resource: K8sResourceKind;
-} & ModalComponentProps;
+} & Required<Pick<ModalComponentProps, 'close'>> &
+  Pick<ModalComponentProps, 'cancel'>;

Also applies to: 97-108

@openshift-ci openshift-ci bot added the component/sdk Related to console-plugin-sdk label Jan 20, 2026
@rhamilto rhamilto changed the title [WIP] CONSOLE-5012: Migrate PVC modals to overlay pattern CONSOLE-5012: Migrate delete modals and PVC modals to overlay pattern Jan 20, 2026
@openshift-ci openshift-ci bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jan 20, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 20, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates PVC-related modals and the general delete modal from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern, following the approach established in #15446.

Changes

Modals Refactored (5 files)

  • delete-pvc-modal.tsx - Added DeletePVCModalProvider using OverlayComponent pattern
  • clone-pvc-modal.tsx - Added ClonePVCModalProvider
  • expand-pvc-modal.tsx - Added ExpandPVCModalProvider
  • restore-pvc-modal.tsx - Added RestorePVCModalProvider
  • delete-modal.tsx - Added DeleteModalProvider (general resource deletion)

Action Creators Converted to Hooks (3 functions)

To work with the overlay system and React's rules of hooks, several action creator functions were converted to hooks:

  • DeleteResourceActionuseDeleteResourceAction in packages/dev-console/src/actions/context-menu.ts
  • DeleteApplicationActionuseDeleteApplicationAction in packages/dev-console/src/actions/context-menu.ts
  • deleteKnativeServiceResourceuseDeleteKnativeServiceResource in packages/knative-plugin/src/actions/creators.ts

Files Updated to Use New Patterns (7 files)

  • useCommonActions.ts - Updated to use LazyDeleteModalProvider with launchModal
  • usePVCActions.ts - Updated to use lazy PVC modal providers
  • useVolumeSnapshotActions.ts - Updated to use LazyRestorePVCModalProvider
  • deployment-provider.ts - Updated to use useDeleteResourceAction hook
  • deploymentconfig-provider.ts - Updated to use useDeleteResourceAction hook
  • knative-plugin/actions/providers.ts - Updated to use useDeleteKnativeServiceResource hook
  • dev-console/actions/providers.tsx - Updated to use useDeleteApplicationAction hook

Infrastructure Updates (2 files)

  • public/components/modals/index.ts - Added lazy-loaded exports for all refactored modal providers using React.lazy() with webpack chunk names
  • packages/console-dynamic-plugin-sdk/src/app/modal-support/OverlayProvider.tsx - Added Suspense wrapper to handle lazy-loaded components

Benefits

  • ✅ Eliminates deprecated createModalLauncher usage for all affected modals
  • ✅ Preserves lazy loading through React.lazy() pattern
  • ✅ Maintains backward compatibility via default exports
  • ✅ Improves code splitting with named webpack chunks
  • ✅ Follows modern React patterns with hooks
  • ✅ All builds, TypeScript compilation, and linting checks pass

Testing

  • Production build succeeds
  • TypeScript compilation passes
  • ESLint passes with no errors
  • No instances of deleteModal() function calls remain
  • All refactored modals use OverlayComponent pattern

Related

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 Jan 20, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates PVC-related modals and the general delete modal from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern, following the approach established in #15446.

Changes

Modals Refactored (5 files)

  • delete-pvc-modal.tsx - Added DeletePVCModalProvider using OverlayComponent pattern
  • clone-pvc-modal.tsx - Added ClonePVCModalProvider
  • expand-pvc-modal.tsx - Added ExpandPVCModalProvider
  • restore-pvc-modal.tsx - Added RestorePVCModalProvider
  • delete-modal.tsx - Added DeleteModalProvider (general resource deletion)

Action Creators Converted to Hooks (3 functions)

To work with the overlay system and React's rules of hooks, several action creator functions were converted to hooks:

  • DeleteResourceActionuseDeleteResourceAction in packages/dev-console/src/actions/context-menu.ts
  • DeleteApplicationActionuseDeleteApplicationAction in packages/dev-console/src/actions/context-menu.ts
  • deleteKnativeServiceResourceuseDeleteKnativeServiceResource in packages/knative-plugin/src/actions/creators.ts

Files Updated to Use New Patterns (7 files)

  • useCommonActions.ts - Updated to use LazyDeleteModalProvider with launchModal
  • usePVCActions.ts - Updated to use lazy PVC modal providers
  • useVolumeSnapshotActions.ts - Updated to use LazyRestorePVCModalProvider
  • deployment-provider.ts - Updated to use useDeleteResourceAction hook
  • deploymentconfig-provider.ts - Updated to use useDeleteResourceAction hook
  • knative-plugin/actions/providers.ts - Updated to use useDeleteKnativeServiceResource hook
  • dev-console/actions/providers.tsx - Updated to use useDeleteApplicationAction hook

Infrastructure Updates (2 files)

  • public/components/modals/index.ts - Added lazy-loaded exports for all refactored modal providers using React.lazy() with webpack chunk names
  • packages/console-dynamic-plugin-sdk/src/app/modal-support/OverlayProvider.tsx - Added Suspense wrapper to handle lazy-loaded components

Benefits

  • ✅ Eliminates deprecated createModalLauncher usage for all affected modals
  • ✅ Preserves lazy loading through React.lazy() pattern
  • ✅ Maintains backward compatibility via default exports
  • ✅ Improves code splitting with named webpack chunks
  • ✅ Follows modern React patterns with hooks
  • ✅ All builds, TypeScript compilation, and linting checks pass

Testing

  • Production build succeeds
  • TypeScript compilation passes
  • ESLint passes with no errors
  • No instances of deleteModal() function calls remain
  • All refactored modals use OverlayComponent pattern

Related

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 component/dashboard Related to dashboard component/dev-console Related to dev-console component/knative Related to knative-plugin component/metal3 Related to metal3-plugin component/monitoring Related to monitoring component/olm Related to OLM component/shared Related to console-shared component/topology Related to topology labels Jan 20, 2026
@rhamilto rhamilto force-pushed the CONSOLE-5012 branch 3 times, most recently from 7354328 to 3dc1895 Compare January 21, 2026 14:38
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 21, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates PVC-related modals and the general delete modal from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern, following the approach established in #15446.

Changes

Modals Refactored (5 files)

  • delete-pvc-modal.tsx - Added DeletePVCModalProvider using OverlayComponent pattern
  • clone-pvc-modal.tsx - Added ClonePVCModalProvider
  • expand-pvc-modal.tsx - Added ExpandPVCModalProvider
  • restore-pvc-modal.tsx - Added RestorePVCModalProvider
  • delete-modal.tsx - Added DeleteModalProvider (general resource deletion)

Action Creators Converted to Hooks (3 functions)

To work with the overlay system and React's rules of hooks, several action creator functions were converted to hooks:

  • DeleteResourceActionuseDeleteResourceAction in packages/dev-console/src/actions/context-menu.ts
  • DeleteApplicationActionuseDeleteApplicationAction in packages/dev-console/src/actions/context-menu.ts
  • deleteKnativeServiceResourceuseDeleteKnativeServiceResource in packages/knative-plugin/src/actions/creators.ts

Files Updated to Use New Patterns (7 files)

  • useCommonActions.ts - Updated to use LazyDeleteModalProvider with launchModal
  • usePVCActions.ts - Updated to use lazy PVC modal providers
  • useVolumeSnapshotActions.ts - Updated to use LazyRestorePVCModalProvider
  • deployment-provider.ts - Updated to use useDeleteResourceAction hook
  • deploymentconfig-provider.ts - Updated to use useDeleteResourceAction hook
  • knative-plugin/actions/providers.ts - Updated to use useDeleteKnativeServiceResource hook
  • dev-console/actions/providers.tsx - Updated to use useDeleteApplicationAction hook

React Hooks Dependency Fixes (3 files)

Fixed infinite loop issues caused by launchModal dependencies in useMemo hooks:

  • useCommonActions.ts - Removed launchModal and launchCountModal from dependency array to prevent max depth exceeded errors
  • usePVCActions.ts - Removed launchModal from dependency array
  • useVolumeSnapshotActions.ts - Removed launchModal from dependency array

The launchModal function is stable and doesn't need to be in the dependency array, as it's returned from useOverlay() which maintains referential stability.

Infrastructure Updates (2 files)

  • public/components/modals/index.ts - Added lazy-loaded exports for all refactored modal providers using React.lazy() with webpack chunk names
  • packages/console-dynamic-plugin-sdk/src/app/modal-support/OverlayProvider.tsx - Added Suspense wrapper to handle lazy-loaded components

Benefits

  • ✅ Eliminates deprecated createModalLauncher usage for all affected modals
  • ✅ Preserves lazy loading through React.lazy() pattern
  • ✅ Maintains backward compatibility via default exports
  • ✅ Improves code splitting with named webpack chunks
  • ✅ Follows modern React patterns with hooks
  • ✅ Fixes React hooks infinite loop issues
  • ✅ All builds, TypeScript compilation, and linting checks pass

Testing

  • Production build succeeds
  • TypeScript compilation passes
  • ESLint passes with no errors
  • No instances of deleteModal() function calls remain
  • All refactored modals use OverlayComponent pattern
  • No infinite loops in action hooks

Related

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.

@rhamilto
Copy link
Member Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 21, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates PVC-related modals and the general delete modal from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern, following the approach established in #15446.

Changes

Modals Refactored (5 files)

  • delete-pvc-modal.tsx - Added DeletePVCModalProvider using OverlayComponent pattern
  • clone-pvc-modal.tsx - Added ClonePVCModalProvider
  • expand-pvc-modal.tsx - Added ExpandPVCModalProvider
  • restore-pvc-modal.tsx - Added RestorePVCModalProvider
  • delete-modal.tsx - Added DeleteModalProvider (general resource deletion)

Action Creators Converted to Hooks (3 functions)

To work with the overlay system and React's rules of hooks, several action creator functions were converted to hooks:

  • DeleteResourceActionuseDeleteResourceAction in packages/dev-console/src/actions/context-menu.ts
  • DeleteApplicationActionuseDeleteApplicationAction in packages/dev-console/src/actions/context-menu.ts
  • deleteKnativeServiceResourceuseDeleteKnativeServiceResource in packages/knative-plugin/src/actions/creators.ts

Files Updated to Use New Patterns (7 files)

  • useCommonActions.ts - Updated to use LazyDeleteModalProvider with launchModal
  • usePVCActions.ts - Updated to use lazy PVC modal providers
  • useVolumeSnapshotActions.ts - Updated to use LazyRestorePVCModalProvider
  • deployment-provider.ts - Updated to use useDeleteResourceAction hook
  • deploymentconfig-provider.ts - Updated to use useDeleteResourceAction hook
  • knative-plugin/actions/providers.ts - Updated to use useDeleteKnativeServiceResource hook
  • dev-console/actions/providers.tsx - Updated to use useDeleteApplicationAction hook

React Hooks Dependency Fixes (3 files)

Fixed infinite loop issues caused by launchModal dependencies in useMemo hooks:

  • useCommonActions.ts - Removed launchModal and launchCountModal from dependency array to prevent max depth exceeded errors
  • usePVCActions.ts - Removed launchModal from dependency array
  • useVolumeSnapshotActions.ts - Removed launchModal from dependency array

The launchModal function is stable and doesn't need to be in the dependency array, as it's returned from useOverlay() which maintains referential stability.

Infrastructure Updates (2 files)

  • public/components/modals/index.ts - Added lazy-loaded exports for all refactored modal providers using React.lazy() with webpack chunk names
  • packages/console-dynamic-plugin-sdk/src/app/modal-support/OverlayProvider.tsx - Added Suspense wrapper to handle lazy-loaded components

Benefits

  • ✅ Eliminates deprecated createModalLauncher usage for all affected modals
  • ✅ Preserves lazy loading through React.lazy() pattern
  • ✅ Maintains backward compatibility via default exports
  • ✅ Improves code splitting with named webpack chunks
  • ✅ Follows modern React patterns with hooks
  • ✅ Fixes React hooks infinite loop issues
  • ✅ All builds, TypeScript compilation, and linting checks pass

Testing

  • Production build succeeds
  • TypeScript compilation passes
  • ESLint passes with no errors
  • No instances of deleteModal() function calls remain
  • All refactored modals use OverlayComponent pattern
  • No infinite loops in action hooks

Related

Summary by CodeRabbit

Release Notes

  • Performance

  • Modal components now load on-demand, reducing initial app startup time.

  • Accessibility

  • Enhanced modal dialog configuration for improved screen reader compatibility and accessibility standards.

  • Improvements

  • Refined modal interaction patterns for resource operations including delete, expand, clone, and restore functions to ensure consistent behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (2)
frontend/public/components/modals/delete-modal.tsx (1)

171-181: Remove the unused DeleteOverlay component.

DeleteOverlay is dead code—it's not imported or referenced anywhere in the codebase. The canonical pattern for this modal is DeleteModalProvider, which correctly uses ModalWrapper and delegates to the OverlayProvider system. This pattern is consistently applied across other modals (expand-pvc-modal, delete-pvc-modal, remove-volume-modal, etc.). Remove DeleteOverlay to reduce confusion and keep the modal architecture clear.

frontend/packages/console-app/src/actions/providers/deployment-provider.ts (1)

17-70: Guard useDeleteResourceAction call against undefined model.

useK8sModel can return undefined when the model is still loading (inFlight=true), but useDeleteResourceAction is invoked unconditionally on line 17 without null-safety checks. The hook immediately dereferences kind.kind in its useMemo (context-menu.ts, line 55), causing a runtime error during model load: Cannot read property 'kind' of undefined.

Move the useDeleteResourceAction call into the deploymentActions useMemo where isReady protects execution, or add a guard: const deleteResourceAction = kindObj ? useDeleteResourceAction(kindObj, resource) : undefined; and handle the conditional safely in the array spread.

🤖 Fix all issues with AI agents
In `@frontend/packages/dev-console/src/actions/context-menu.ts`:
- Around line 52-64: Remove launchModal from the useMemo dependency array and
add the standard ESLint suppression to prevent the exhaustive-deps warning;
specifically, keep the memoized object creation (id, label, cta calling
launchModal(LazyDeleteModalProvider, { kind, resource: obj, deleteAllResources:
() => cleanUpWorkload(obj) }), accessReview: asAccessReview(kind, obj,
'delete')) but change the dependency list to [t, kind, obj] and place the
single-line ESLint disable comment (react-hooks/exhaustive-deps) immediately
above the dependency array to match the established pattern.
- Around line 27-40: The delete flow in useDeleteApplicationAction still uses
the legacy deleteResourceModal; refactor it to use the overlay pattern by
replacing deleteResourceModal(...) with launchModal(LazyDeleteModalProvider,
...) mirroring the approach in useDeleteResourceAction: pass equivalent
parameters (blocking, resourceName, resourceType) and wire the onSubmit to call
cleanUpWorkload for each item in application.resources and return Promise.all of
those promises; ensure the modal provider receives the same confirmation/props
shape as used by LazyDeleteModalProvider so behavior matches the existing delete
path.

In `@frontend/public/components/modals/expand-pvc-modal.tsx`:
- Around line 99-105: The spread {...props} in ExpandPVCModalProvider can
overwrite the explicit cancel and close bindings; update the provider so the
modal receives the overlay-controlled handlers last (or omit cancel/close from
the spread) — locate ExpandPVCModalProvider and change the JSX to either spread
props before passing cancel={props.closeOverlay} close={props.closeOverlay}, or
destructure props to remove cancel/close (e.g. const { cancel, close, ...rest }
= props) and then render <ExpandPVCModal {...rest} cancel={props.closeOverlay}
close={props.closeOverlay} /> so the overlay's closeOverlay always wins.
♻️ Duplicate comments (1)
frontend/packages/console-app/src/actions/providers/deploymentconfig-provider.ts (1)

18-69: Same model-availability concern as deployment-provider.

This file uses the same useDeleteResourceAction(kindObj, resource) path. Please ensure kindObj is never undefined during render, for the same reason noted in deployment-provider (context-menu.ts Lines 47-65 dereferences kind.kind).

🧹 Nitpick comments (7)
frontend/packages/console-app/src/actions/providers/machine-config-pool-provider.ts (1)

15-29: Avoid stale launchModal by stabilizing it instead of disabling deps.

launchModal is captured in cta, but omitted from the dependency list. If useOverlay() ever returns a new function (e.g., provider changes), the memoized action can call a stale reference. Prefer stabilizing the handler (ref/useEvent) and keep deps correct rather than disabling the rule.

♻️ Suggested refactor (stable ref, no eslint-disable)
-import { useMemo } from 'react';
+import { useEffect, useMemo, useRef } from 'react';
@@
   const launchModal = useOverlay();
+  const launchModalRef = useRef(launchModal);
+  useEffect(() => {
+    launchModalRef.current = launchModal;
+  }, [launchModal]);
@@
-        cta: () =>
-          togglePaused(MachineConfigPoolModel, obj).catch((err) =>
-            launchModal(ErrorModal, { error: err.message }),
-          ),
+        cta: () =>
+          togglePaused(MachineConfigPoolModel, obj).catch((err) =>
+            launchModalRef.current(ErrorModal, { error: err.message }),
+          ),
@@
-    // missing launchModal dependency, that causes max depth exceeded error
-    // eslint-disable-next-line react-hooks/exhaustive-deps
     [obj, t],
   );
frontend/packages/console-dynamic-plugin-sdk/src/app/modal-support/OverlayProvider.tsx (1)

55-57: Consider a non‑null Suspense fallback for overlay UX/a11y.

With fallback={null}, a lazy overlay can appear to do nothing on launch; screen readers also get no loading status. Consider using the project’s standard loading component (PatternFly spinner/LoadingInline/etc.) as the fallback to provide feedback while the overlay code is fetched. Please verify the desired behavior with React Suspense semantics in this app.

frontend/packages/console-app/src/actions/providers/build-config-provider.ts (1)

36-38: The eslint-disable here is justified, but the assumption needs correction.

The launchOverlay function returned by useOverlay() is already referentially stable—it's wrapped in useCallback with an empty dependency array in OverlayProvider (line 40). The reason for omitting it isn't instability; including it creates a circular dependency pattern that triggers the "max depth exceeded" infinite re-render loop. This is a pragmatic but fragile workaround.

Since the pattern is consistent across this codebase (usePVCActions, useVolumeSnapshotActions, useCommonActions), consider whether the root cause—why modal closures create circular deps—should be addressed at the hook level rather than suppressed across multiple consumers. If this is the intended design, document it in useOverlay's JSDoc to help future maintainers understand why consumers must skip this dependency.

frontend/public/components/modals/expand-pvc-modal.tsx (1)

35-52: Consider handling the patch failure case.

The submit callback chains .then() without a .catch() or error handling branch. While usePromiseHandler captures the error for display via errorMessage, the promise chain could be clearer about the failure path—particularly since close() and navigate() only run on success.

The current implementation is functionally correct since handlePromise manages errors internally, but explicit handling improves readability for future maintainers.

frontend/packages/dev-console/src/actions/context-menu.ts (1)

1-11: Unused import: deleteResourceModal still imported but only used in one hook.

If useDeleteApplicationAction is migrated to the overlay pattern, the deleteResourceModal import (line 9) can be removed entirely. Currently it creates a mixed dependency footprint.

frontend/public/components/modals/index.ts (1)

89-94: Inconsistent export resolution in lazy PVC modal providers.

LazyClonePVCModalProvider resolves via named export m.ClonePVCModalProvider, while LazyExpandPVCModalProvider and LazyRestorePVCModalProvider resolve via m.default. While all three source files export both the named export and a default (so no functional breakage), this inconsistency reduces clarity and makes the code harder to maintain.

Consider standardizing on one approach—either use default exports consistently, or use named exports consistently across all three providers.

frontend/packages/knative-plugin/src/actions/providers.ts (1)

98-151: Consider collapsing the two delete‑action hooks into one. This trims duplicate hook work and simplifies the dependency list without changing behavior.

♻️ Suggested simplification
-  const deleteKnativeServiceFromWebAction = useDeleteKnativeServiceResource(
-    kindObj,
-    resource,
-    serviceTypeValue,
-    true,
-  );
-  const deleteKnativeServiceAction = useDeleteKnativeServiceResource(
-    kindObj,
-    resource,
-    serviceTypeValue,
-    false,
-  );
+  const createdFromWeb =
+    resource?.metadata?.annotations?.['openshift.io/generated-by'] === 'OpenShiftWebConsole';
+  const deleteKnativeServiceAction = useDeleteKnativeServiceResource(
+    kindObj,
+    resource,
+    serviceTypeValue,
+    createdFromWeb,
+  );
@@
-            ...(resource.metadata.annotations?.['openshift.io/generated-by'] ===
-            'OpenShiftWebConsole'
-              ? [deleteKnativeServiceFromWebAction]
-              : [deleteKnativeServiceAction]),
+            deleteKnativeServiceAction,
@@
-      deleteKnativeServiceFromWebAction,
-      deleteKnativeServiceAction,
+      deleteKnativeServiceAction,

Comment on lines 27 to 40
cta: () => {
const reqs = [];
deleteResourceModal({
blocking: true,
resourceName: application.name,
resourceType: ApplicationModel.label,
onSubmit: () => {
application.resources.forEach((resource) => {
reqs.push(cleanUpWorkload(resource.resource));
});
return Promise.all(reqs);
},
});
},
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n frontend/packages/dev-console/src/actions/context-menu.ts | head -70

Repository: openshift/console

Length of output: 178


🏁 Script executed:

rg -n "deleteResourceModal" --type ts -g '!*.test.*' | head -30

Repository: openshift/console

Length of output: 178


🏁 Script executed:

rg -n "launchModal.*LazyDeleteModalProvider" --type ts -g '!*.test.*' | head -20

Repository: openshift/console

Length of output: 288


🏁 Script executed:

rg -n "deleteResourceModal\|launchModal" frontend/packages/dev-console/src/actions/context-menu.ts -A 2 -B 2

Repository: openshift/console

Length of output: 43


🏁 Script executed:

head -20 frontend/packages/dev-console/src/actions/context-menu.ts

Repository: openshift/console

Length of output: 1053


🏁 Script executed:

sed -n '13,100p' frontend/packages/dev-console/src/actions/context-menu.ts

Repository: openshift/console

Length of output: 1746


🏁 Script executed:

rg "deleteResourceModal" frontend/packages/dev-console/src/actions/context-menu.ts -B 2 -A 5

Repository: openshift/console

Length of output: 742


Inconsistent modal pattern in delete actions.

useDeleteApplicationAction continues using the legacy deleteResourceModal from @console/shared, while useDeleteResourceAction below it uses the new launchModal(LazyDeleteModalProvider, ...) overlay pattern. Both export delete actions from the same file, creating inconsistent modal patterns.

Given the PR's stated objective to migrate delete modals to the overlay pattern, clarify whether the legacy deleteResourceModal should be refactored to use launchModal(LazyDeleteModalProvider, ...) with appropriate parameters (similar to useDeleteResourceAction), or if this pattern difference is intentional.

🤖 Prompt for AI Agents
In `@frontend/packages/dev-console/src/actions/context-menu.ts` around lines 27 -
40, The delete flow in useDeleteApplicationAction still uses the legacy
deleteResourceModal; refactor it to use the overlay pattern by replacing
deleteResourceModal(...) with launchModal(LazyDeleteModalProvider, ...)
mirroring the approach in useDeleteResourceAction: pass equivalent parameters
(blocking, resourceName, resourceType) and wire the onSubmit to call
cleanUpWorkload for each item in application.resources and return Promise.all of
those promises; ensure the modal provider receives the same confirmation/props
shape as used by LazyDeleteModalProvider so behavior matches the existing delete
path.

Comment on lines 52 to 64
return useMemo(
() => ({
id: `delete-resource`,
label: t('devconsole~Delete {{kind}}', { kind: kind.kind }),
cta: () =>
launchModal(LazyDeleteModalProvider, {
kind,
resource: obj,
deleteAllResources: () => cleanUpWorkload(obj),
}),
accessReview: asAccessReview(kind, obj, 'delete'),
}),
accessReview: asAccessReview(kind, obj, 'delete'),
});
[t, kind, obj, launchModal],
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check how other files in console-app handle launchModal in dependency arrays
rg -n "launchModal" -A 3 -B 1 frontend/packages/console-app/src/actions/ | head -100

Repository: openshift/console

Length of output: 178


🏁 Script executed:

# Also check the dev-console package for comparison
rg -n "launchModal" -A 3 -B 1 frontend/packages/dev-console/src/actions/ | head -150

Repository: openshift/console

Length of output: 1234


🏁 Script executed:

# Search for eslint-disable comments related to launchModal across the repo
rg "eslint-disable.*launchModal|launchModal.*eslint-disable" -n frontend/

Repository: openshift/console

Length of output: 43


🏁 Script executed:

# Find the useOverlay hook definition to understand launchModal's stability
fd -e ts -e tsx | xargs rg -l "useOverlay" | head -5

Repository: openshift/console

Length of output: 426


🏁 Script executed:

# Find useOverlay hook definition
rg -n "export.*useOverlay|function useOverlay" -A 5 frontend/

Repository: openshift/console

Length of output: 1418


🏁 Script executed:

# Check how other packages use launchModal in useMemo/useCallback
rg -B 5 -A 5 "useMemo|useCallback" frontend/packages/ | rg -B 5 -A 5 "launchModal" | head -200

Repository: openshift/console

Length of output: 19822


🏁 Script executed:

# Specifically check knative and metal3 actions for pattern
cat frontend/packages/knative-plugin/src/actions/creators.ts 2>/dev/null | head -100

Repository: openshift/console

Length of output: 4242


Remove launchModal from the dependency array.

The established pattern across the codebase (console-app, knative-plugin) excludes launchModal from useMemo dependency arrays with an ESLint disable comment citing "missing launchModal dependency, that causes max depth exceeded error." This avoids circular dependency issues. Align with this pattern by removing launchModal from [t, kind, obj, launchModal] and adding the standard ESLint disable.

🤖 Prompt for AI Agents
In `@frontend/packages/dev-console/src/actions/context-menu.ts` around lines 52 -
64, Remove launchModal from the useMemo dependency array and add the standard
ESLint suppression to prevent the exhaustive-deps warning; specifically, keep
the memoized object creation (id, label, cta calling
launchModal(LazyDeleteModalProvider, { kind, resource: obj, deleteAllResources:
() => cleanUpWorkload(obj) }), accessReview: asAccessReview(kind, obj,
'delete')) but change the dependency list to [t, kind, obj] and place the
single-line ESLint disable comment (react-hooks/exhaustive-deps) immediately
above the dependency array to match the established pattern.

Comment on lines 99 to 105
export const ExpandPVCModalProvider: OverlayComponent<ExpandPVCModalProps> = (props) => {
return (
<ModalWrapper blocking onClose={props.closeOverlay}>
<ExpandPVCModal cancel={props.closeOverlay} close={props.closeOverlay} {...props} />
</ModalWrapper>
);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Props spread order may override cancel/close bindings.

The spread {...props} comes after the explicit cancel={props.closeOverlay} and close={props.closeOverlay}. If the caller passes cancel or close in props, they will override the closeOverlay bindings, which is likely unintended since the overlay system expects to control modal dismissal.

Proposed fix
 export const ExpandPVCModalProvider: OverlayComponent<ExpandPVCModalProps> = (props) => {
   return (
     <ModalWrapper blocking onClose={props.closeOverlay}>
-      <ExpandPVCModal cancel={props.closeOverlay} close={props.closeOverlay} {...props} />
+      <ExpandPVCModal {...props} cancel={props.closeOverlay} close={props.closeOverlay} />
     </ModalWrapper>
   );
 };
📝 Committable suggestion

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

Suggested change
export const ExpandPVCModalProvider: OverlayComponent<ExpandPVCModalProps> = (props) => {
return (
<ModalWrapper blocking onClose={props.closeOverlay}>
<ExpandPVCModal cancel={props.closeOverlay} close={props.closeOverlay} {...props} />
</ModalWrapper>
);
};
export const ExpandPVCModalProvider: OverlayComponent<ExpandPVCModalProps> = (props) => {
return (
<ModalWrapper blocking onClose={props.closeOverlay}>
<ExpandPVCModal {...props} cancel={props.closeOverlay} close={props.closeOverlay} />
</ModalWrapper>
);
};
🤖 Prompt for AI Agents
In `@frontend/public/components/modals/expand-pvc-modal.tsx` around lines 99 -
105, The spread {...props} in ExpandPVCModalProvider can overwrite the explicit
cancel and close bindings; update the provider so the modal receives the
overlay-controlled handlers last (or omit cancel/close from the spread) — locate
ExpandPVCModalProvider and change the JSX to either spread props before passing
cancel={props.closeOverlay} close={props.closeOverlay}, or destructure props to
remove cancel/close (e.g. const { cancel, close, ...rest } = props) and then
render <ExpandPVCModal {...rest} cancel={props.closeOverlay}
close={props.closeOverlay} /> so the overlay's closeOverlay always wins.

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 21, 2026
@openshift-ci openshift-ci bot added the kind/cypress Related to Cypress e2e integration testing label Jan 21, 2026
Migrates PVC-related modals (expand, clone, delete, restore) from the
legacy createModalLauncher pattern to the modern OverlayComponent pattern
with lazy loading.

Changes include:
- Convert modal exports to OverlayComponent providers
- Implement lazy loading for modal components
- Update action hooks to use useOverlay() and launchModal()
- Set react-modal app element globally in App component
- Maintain backward compatibility with existing modal exports

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jan 21, 2026
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 21, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: rhamilto

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

The pull request process is described 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-robot
Copy link
Contributor

openshift-ci-robot commented Jan 21, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

This PR migrates PVC-related modals and the general delete modal from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern with lazy loading, and fixes launchModal dependency issues across the codebase.

Changes

Commit 1: Migrate PVC modals to overlay pattern (10 files)

Modals Refactored (4 files)

  • delete-pvc-modal.tsx - Added DeletePVCModalProvider using OverlayComponent pattern
  • clone-pvc-modal.tsx - Added ClonePVCModalProvider
  • expand-pvc-modal.tsx - Added ExpandPVCModalProvider
  • restore-pvc-modal.tsx - Added RestorePVCModalProvider

Action Hooks Updated (2 files)

  • usePVCActions.ts - Updated to use lazy PVC modal providers with launchModal
  • useVolumeSnapshotActions.ts - Updated to use LazyRestorePVCModalProvider

Infrastructure Updates (4 files)

  • public/components/modals/index.ts - Added lazy-loaded exports for PVC modal providers
  • OverlayProvider.tsx - Added Suspense wrapper to handle lazy-loaded components
  • app.tsx - Set react-modal app element globally
  • factory/modal.tsx - Minor adjustment for modal configuration

Commit 2: Migrate delete-modal and actions to overlay pattern (8 files)

Modal Refactored (1 file)

  • delete-modal.tsx - Added DeleteModalProvider using OverlayComponent pattern with lazy loading

Action Creators Converted to Hooks (3 functions)
To work with the overlay system and React's rules of hooks, several action creator functions were converted to hooks:

  • DeleteResourceActionuseDeleteResourceAction in dev-console/src/actions/context-menu.ts
  • DeleteApplicationActionuseDeleteApplicationAction in dev-console/src/actions/context-menu.ts
  • deleteKnativeServiceResourceuseDeleteKnativeServiceResource in knative-plugin/src/actions/creators.ts

Files Updated to Use New Patterns (6 files)

  • useCommonActions.ts - Updated to use LazyDeleteModalProvider with launchModal
  • deployment-provider.ts - Updated to use useDeleteResourceAction hook
  • deploymentconfig-provider.ts - Updated to use useDeleteResourceAction hook
  • knative-plugin/actions/providers.ts - Updated to use useDeleteKnativeServiceResource hook
  • dev-console/actions/providers.tsx - Updated to use useDeleteApplicationAction hook
  • public/components/modals/index.ts - Added lazy-loaded export for DeleteModalProvider

Commit 3: Fix launchModal dependency in existing action hooks (12 files)

Fixed infinite loop/"maximum depth exceeded" issues caused by including launchModal in React hook dependency arrays. The launchModal function from useOverlay() is stable and doesn't need to be in dependency arrays.

Action Hooks Fixed (5 files)

  • useBuildsActions.ts - Removed launchModal from useMemo factory deps
  • useDeploymentActions.ts - Removed launchModal from useMemo factory deps
  • useJobActions.ts - Removed launchModal from useMemo factory deps
  • useRetryRolloutAction.ts - Removed launchModal from useMemo deps
  • usePVCActions.ts - Removed launchModal from useMemo deps (from Commit 1)

Action Providers Fixed (2 files)

  • build-config-provider.ts - Removed launchModal from two useMemo factory deps
  • machine-config-pool-provider.ts - Removed launchModal from useMemo factory deps

Shipwright Plugin Fixed (2 files)

  • useBuildActions.ts - Removed launchModal from useMemo deps
  • useBuildRunActions.ts - Removed launchModal from useMemo deps

Components Fixed (3 files)

  • EnvironmentField.tsx - Removed launchModal from useEffect deps
  • SecretKeySelector.tsx - Removed launchModal from useEffect deps
  • webhooks.tsx - Removed launchModal from useEffect deps

Prop Ordering Fix (1 file)

  • expand-pvc-modal.tsx - Fixed prop spread order to ensure overlay handlers take precedence

All fixes include the standard ESLint suppression comment:

// missing launchModal dependency, that causes max depth exceeded error
// eslint-disable-next-line react-hooks/exhaustive-deps

Benefits

  • ✅ Eliminates deprecated createModalLauncher usage for all PVC and delete modals
  • ✅ Preserves lazy loading through React.lazy() pattern with named webpack chunks
  • ✅ Maintains backward compatibility via default exports
  • ✅ Follows modern React patterns with hooks
  • ✅ Fixes React hooks infinite loop issues across 11 existing hooks
  • ✅ Prevents "maximum depth exceeded" errors
  • ✅ All builds, TypeScript compilation, and linting checks pass

Testing

  • Production build succeeds
  • TypeScript compilation passes
  • ESLint passes with no errors
  • No instances of legacy modal patterns remain for affected modals
  • All refactored modals use OverlayComponent pattern
  • No infinite loops in action hooks
  • Pre-commit hooks pass

Related

Files Changed Summary

Total: 26 unique files modified

  • 10 files in Commit 1 (PVC modals migration)
  • 8 files in Commit 2 (Delete modal migration)
  • 12 files in Commit 3 (launchModal dependency fixes)
  • 4 files modified across multiple commits (usePVCActions, index.ts, expand-pvc-modal, useCommonActions)

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.

rhamilto and others added 2 commits January 21, 2026 14:24
This commit completes the migration from createModalLauncher to the
OverlayComponent pattern for delete modals and related actions.

Changes:
- Refactored delete-modal.tsx to use OverlayComponent pattern
- Added LazyDeleteModalProvider with React.lazy() for code splitting
- Converted action creators to hooks to comply with React rules:
  * DeleteResourceAction → useDeleteResourceAction (context-menu.ts)
  * deleteKnativeServiceResource → useDeleteKnativeServiceResource (creators.ts)
- Updated all consumers to use new hook-based actions:
  * useCommonActions - uses LazyDeleteModalProvider with launchModal
  * deployment-provider - uses useDeleteResourceAction hook
  * deploymentconfig-provider - uses useDeleteResourceAction hook
  * knative-plugin providers - uses useDeleteKnativeServiceResource hook
- Updated public/components/modals/index.ts with lazy export
- Eliminated all deleteModal() function calls

Note: DeleteApplicationAction remains as a non-hook function creator
since it doesn't use modals and doesn't need to comply with hooks rules.

This change maintains backward compatibility through default exports
and preserves lazy loading functionality.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove launchModal from dependency arrays in pre-existing hooks to
prevent infinite loops. These files were not modified as part of the
modal migration work, but had the same issue.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 21, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

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.

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 21, 2026

@rhamilto: This pull request references CONSOLE-5012 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.22.0" version, but no target version was set.

Details

In response to this:

Summary

Migrates delete modals and PVC-related modals from the deprecated createModalLauncher pattern to the modern OverlayComponent pattern. This is part of CONSOLE-5012 to modernize modal handling across the console.

Changes

Modals Migrated (5 total)

All modals now follow the OverlayComponent pattern with consistent implementation:

  1. DeleteModal (public/components/modals/delete-modal.tsx)
  • Wrapped with DeleteModalProvider: OverlayComponent
  • Exported as LazyDeleteModalProvider with lazy loading
  1. DeletePVCModal (public/components/modals/delete-pvc-modal.tsx)
  • Wrapped with DeletePVCModalProvider: OverlayComponent
  • Includes PVC-specific deletion logic
  1. ExpandPVCModal (public/components/modals/expand-pvc-modal.tsx)
  • Wrapped with ExpandPVCModalProvider: OverlayComponent
  • Exported as LazyExpandPVCModalProvider
  1. ClonePVCModal (packages/console-app/src/components/modals/clone/clone-pvc-modal.tsx)
  • Wrapped with ClonePVCModalProvider: OverlayComponent
  • Exported as LazyClonePVCModalProvider
  1. RestorePVCModal (packages/console-app/src/components/modals/restore-pvc/restore-pvc-modal.tsx)
  • Wrapped with RestorePVCModalProvider: OverlayComponent
  • Exported as LazyRestorePVCModalProvider

Action Creators Converted to Hooks (3 total)

To integrate with the React hooks system and useOverlay() pattern:

  1. useDeleteResourceAction (packages/dev-console/src/actions/context-menu.ts)
  • Converted from DeleteResourceAction function to hook
  • Uses useOverlay() and LazyDeleteModalProvider
  • Updated consumers: deployment-provider.ts, deploymentconfig-provider.ts
  1. useDeleteApplicationAction (packages/dev-console/src/actions/context-menu.ts)
  • Converted from function to hook (existing)
  • Uses deleteResourceModal from @console/shared
  1. useDeleteKnativeServiceResource (packages/knative-plugin/src/actions/creators.ts)
  • Converted from deleteKnativeServiceResource function to hook
  • Conditional cleanUpWorkload based on serviceCreatedFromWebFlag
  • Updated consumer: knative-plugin/src/actions/providers.ts

React Hooks Dependency Fixes (Critical)

Fixed infinite loop issues caused by launchModal in dependency arrays:

  • useCommonActions.ts: Removed launchModal and launchCountModal from dependency array
  • usePVCActions.ts: Removed launchModal from dependency array
  • useVolumeSnapshotActions.ts: Removed launchModal from dependency array

Rationale: launchModal from useOverlay() maintains referential stability and doesn't need to be in dependency arrays. Including it causes "max depth exceeded" errors.

Infrastructure Updates

  • OverlayProvider.tsx: Added <Suspense> wrapper for lazy-loaded overlay components
  • modals/index.ts: Updated exports to use consistent React.lazy() pattern with all modals using .then((m) => ({ default: m.default }))

Pattern Example

// Modal file
export const DeleteModalProvider: OverlayComponent<DeleteModalProps> = (props) => {
 return (
   <ModalWrapper blocking onClose={props.closeOverlay}>
     <DeleteModal cancel={props.closeOverlay} close={props.closeOverlay} {...props} />
   </ModalWrapper>
 );
};

export default DeleteModalProvider;
// index.ts
export const LazyDeleteModalProvider = lazy(() =>
 import('./delete-modal').then((m) => ({ default: m.default })),
);
// Action hook
export const useDeleteResourceAction = (kind: K8sModel, obj: K8sResourceKind): Action => {
 const { t } = useTranslation();
 const launchModal = useOverlay();

 return useMemo(
   () => ({
     id: 'delete-resource',
     label: t('devconsole~Delete {{kind}}', { kind: kind.kind }),
     cta: () => launchModal(LazyDeleteModalProvider, { kind, resource: obj }),
     accessReview: asAccessReview(kind, obj, 'delete'),
   }),
   [t, kind, obj, launchModal], // launchModal is stable but included for completeness
 );
};

Benefits

  • ✅ Consistent pattern across all modals
  • ✅ Better code splitting with lazy loading
  • ✅ Eliminates infinite re-render bugs
  • ✅ Proper React hooks integration
  • ✅ Maintains backward compatibility with default exports
  • ✅ Improved maintainability

Testing

  • All 5 modals open and close correctly
  • Delete actions work for Deployments, DeploymentConfigs, and Knative Services
  • PVC actions work (expand, clone, restore, delete)
  • No console errors or infinite loops
  • Lazy loading works correctly (check Network tab for separate chunks)

Related

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
Copy link
Contributor

openshift-ci bot commented Jan 22, 2026

@rhamilto: 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.

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. component/core Related to console core functionality component/dashboard Related to dashboard component/dev-console Related to dev-console component/knative Related to knative-plugin component/metal3 Related to metal3-plugin component/monitoring Related to monitoring component/olm Related to OLM component/sdk Related to console-plugin-sdk component/shared Related to console-shared component/topology Related to topology jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. kind/cypress Related to Cypress e2e integration testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants