Skip to content

CONSOLE-5012: Create shared error modal launcher using React Context#15946

Open
rhamilto wants to merge 1 commit intoopenshift:mainfrom
rhamilto:CONSOLE-5012-4
Open

CONSOLE-5012: Create shared error modal launcher using React Context#15946
rhamilto wants to merge 1 commit intoopenshift:mainfrom
rhamilto:CONSOLE-5012-4

Conversation

@rhamilto
Copy link
Member

@rhamilto rhamilto commented Jan 22, 2026

Summary

Creates a shared error modal launcher utility in the console-shared package using React Context to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Architecture

React Context Pattern

This implementation reuses the existing OverlayProvider instead of creating a new context provider:

<OverlayProvider>
  <SyncErrorModalLauncher />  {/* Zero-render sync component */}
  <App />
</OverlayProvider>

Dual API for Different Contexts

React Components - Use the hook:

const launchErrorModal = useErrorModalLauncher();
launchErrorModal({ title: 'Error', error: err.message });

Non-React Code (callbacks, promises, utilities) - Use the function:

createConnection(source, target).catch(err => {
  launchErrorModal({ title: 'Failed', error: err.message });
});

Changes

New Shared Utility

packages/console-shared/src/utils/error-modal-handler.tsx

  • SyncErrorModalLauncher - Component that syncs the launcher for non-React contexts
  • useErrorModalLauncher() - Hook for launching error modals from React components
  • launchErrorModal() - Function for launching from non-React contexts (callbacks, promises)

Test Coverage

packages/console-shared/src/utils/__tests__/error-modal-handler.spec.tsx

  • Unit tests covering all scenarios (5 tests, all passing)
  • Tests lifecycle (mount/unmount)
  • Tests error handling (uninitialized launcher)

packages/console-shared/src/utils/__mocks__/error-modal-handler.tsx

  • Mock utilities for testing components that use the error modal launcher

Single Initialization Point

public/components/app.tsx

  • Added <SyncErrorModalLauncher /> inside OverlayProvider
  • Single initialization point for the entire application

Migrated to Shared Pattern

packages/topology/src/utils/moveNodeToGroup.tsx

  • Migrated from custom globalErrorHandler to shared launchErrorModal()
  • Removed setMoveNodeToGroupErrorHandler() and useSetupMoveNodeToGroupErrorHandler()
  • Removed deprecated useMoveNodeToGroup() hook
  • Now uses consistent pattern with rest of codebase

packages/topology/src/components/graph-view/Topology.tsx

  • Removed useSetupMoveNodeToGroupErrorHandler() initialization (no longer needed)

Migrated to New API

Updated to use launchErrorModal in:

  • topology: componentUtils.ts, edgeActions.ts, moveNodeToGroup.tsx
  • knative-plugin: knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: logs-utils.ts
  • dev-console: pipeline-template-utils.ts

Cleanup

  • Removed deprecated errorModal function from public/components/modals
  • Removed redundant resetErrorModalMocks() helper from mock file
  • Updated exports in console-shared package

Benefits

  • Eliminates global state race conditions - Single initialization point in app root
  • Consistent pattern across codebase - All error modals use the same shared utility
  • Improves testability - OverlayProvider context can be mocked in tests
  • No code duplication - Shared utility used across all packages
  • Reuses existing infrastructure - No new providers, uses existing OverlayProvider
  • Simpler architecture - No unnecessary provider nesting or custom error handlers
  • Type-safe - Full TypeScript support with proper generics
  • Proper lifecycle - Cleanup on unmount via useEffect
  • Backward compatible - Non-React code continues to work via module reference
  • Comprehensive test coverage - 5 unit tests covering all scenarios

Test Plan

Automated Tests

  • ✅ Unit tests pass (5/5 tests)
  • ✅ TypeScript compilation succeeds
  • ✅ ESLint passes
  • ✅ Pre-commit hooks pass

Manual Testing

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered
  • Test error modal from React component using useErrorModalLauncher() hook
  • Test error modal from promise chain using launchErrorModal() function
  • Test topology node move operations with error handling

Technical Details

Hybrid Approach

The implementation uses a hybrid pattern to support both React and non-React code:

  1. React Components: Call useErrorModalLauncher() hook which directly uses useOverlay()
  2. Non-React Code: Call launchErrorModal() function which accesses a module-level reference
  3. Sync Component: SyncErrorModalLauncher syncs the launcher to the module-level variable

This approach:

  • Eliminates the need for a custom Context provider
  • Reuses the existing OverlayProvider infrastructure
  • Supports both React hooks and imperative API calls
  • Has proper cleanup via React lifecycle

Migration Path

Before:

  • No shared utility
  • Each package implemented its own error modal handling
  • Inconsistent patterns across codebase (custom global handlers, direct imports)
  • Direct imports of deprecated errorModal function

After:

  • Single initialization point (app.tsx)
  • React Context-based (via OverlayProvider)
  • Proper cleanup on unmount
  • Easy to test with mocks
  • Consistent API across all packages
  • No custom error handler patterns

Related

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added improved error modal handling system with enhanced initialization and non-React context support
    • Added new error message for connector deletion workflows
  • Bug Fixes

    • Enhanced error handling in drag-and-drop and routing operations to use centralized launcher
  • Tests

    • Added comprehensive test coverage for new error modal utilities and handlers
  • Refactor

    • Migrated error modal usage across multiple components to use new centralized launcher
    • Removed deprecated error modal implementation in favor of new handler

@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 22, 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 Jan 22, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 22, 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

Creates a shared global error modal launcher utility in the console-shared package to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Changes

New Shared Utility

  • packages/console-shared/src/utils/error-modal-handler.ts
  • launchGlobalErrorModal() - Launch error modals from non-React contexts
  • useSetupGlobalErrorModalLauncher() - Hook to initialize the launcher
  • setGlobalErrorModalLauncher() - Manage global state

Migrated Packages

  • topology: Updated Topology.tsx, componentUtils.ts, edgeActions.ts
  • knative-plugin: Updated knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: Updated logs-utils.ts, BuildRunDetailsPage.tsx
  • dev-console: Updated pipeline-template-utils.ts, ImportPage.tsx, DeployImagePage.tsx

Launcher Initialization

Added useSetupGlobalErrorModalLauncher() in root components:

  • packages/topology/src/components/graph-view/Topology.tsx - For topology drag/drop contexts
  • packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx - For standalone log viewing
  • packages/dev-console/src/components/import/ImportPage.tsx - For Git import flow
  • packages/dev-console/src/components/import/DeployImagePage.tsx - For deploy image flow

Cleanup

  • Removed topology-specific errorModalHandler implementation
  • Removed deprecated errorModal function from public/components/modals

Benefits

  • ✅ Eliminates code duplication across packages
  • ✅ Ensures error modals work in all contexts (topology, standalone pages, import flows)
  • ✅ Single source of truth for error modal launching
  • ✅ Proper initialization and cleanup via React hooks

Test plan

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered

Related

Jira: CONSOLE-5012

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

openshift-ci bot commented Jan 22, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 22, 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

Creates a shared global error modal launcher utility in the console-shared package to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Changes

New Shared Utility

  • packages/console-shared/src/utils/error-modal-handler.ts
  • launchGlobalErrorModal() - Launch error modals from non-React contexts
  • useSetupGlobalErrorModalLauncher() - Hook to initialize the launcher
  • setGlobalErrorModalLauncher() - Manage global state

Migrated Packages

  • topology: Updated Topology.tsx, componentUtils.ts, edgeActions.ts
  • knative-plugin: Updated knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: Updated logs-utils.ts, BuildRunDetailsPage.tsx
  • dev-console: Updated pipeline-template-utils.ts, ImportPage.tsx, DeployImagePage.tsx

Launcher Initialization

Added useSetupGlobalErrorModalLauncher() in root components:

  • packages/topology/src/components/graph-view/Topology.tsx - For topology drag/drop contexts
  • packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx - For standalone log viewing
  • packages/dev-console/src/components/import/ImportPage.tsx - For Git import flow
  • packages/dev-console/src/components/import/DeployImagePage.tsx - For deploy image flow

Cleanup

  • Removed topology-specific errorModalHandler implementation
  • Removed deprecated errorModal function from public/components/modals

Benefits

  • ✅ Eliminates code duplication across packages
  • ✅ Ensures error modals work in all contexts (topology, standalone pages, import flows)
  • ✅ Single source of truth for error modal launching
  • ✅ Proper initialization and cleanup via React hooks

Test plan

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered

Related

Jira: CONSOLE-5012

🤖 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 added component/core Related to console core functionality component/dev-console Related to dev-console approved Indicates a PR has been approved by an approver from all required OWNERS files. component/knative Related to knative-plugin component/sdk Related to console-plugin-sdk component/shared Related to console-shared component/topology Related to topology labels Jan 22, 2026
@rhamilto rhamilto marked this pull request as ready for review January 23, 2026 15:17
@rhamilto rhamilto changed the title WIP: CONSOLE-5012: Create shared global error modal launcher utility [WIP] CONSOLE-5012: Create shared global error modal launcher utility Jan 23, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Jan 23, 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

Creates a shared global error modal launcher utility in the console-shared package to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Note: This PR is based on CONSOLE-5012 and includes changes from other PRs in that branch. The changes below are cumulative and may include work from multiple related PRs.

Changes

New Shared Utility

  • packages/console-shared/src/utils/error-modal-handler.ts
  • launchGlobalErrorModal() - Launch error modals from non-React contexts
  • useSetupGlobalErrorModalLauncher() - Hook to initialize the launcher
  • setGlobalErrorModalLauncher() - Manage global state

Migrated Packages

  • topology: Updated Topology.tsx, componentUtils.ts, edgeActions.ts
  • knative-plugin: Updated knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: Updated logs-utils.ts, BuildRunDetailsPage.tsx
  • dev-console: Updated pipeline-template-utils.ts, ImportPage.tsx, DeployImagePage.tsx

Launcher Initialization

Added useSetupGlobalErrorModalLauncher() in root components:

  • packages/topology/src/components/graph-view/Topology.tsx - For topology drag/drop contexts
  • packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx - For standalone log viewing
  • packages/dev-console/src/components/import/ImportPage.tsx - For Git import flow
  • packages/dev-console/src/components/import/DeployImagePage.tsx - For deploy image flow

Cleanup

  • Removed topology-specific errorModalHandler implementation
  • Removed deprecated errorModal function from public/components/modals

Benefits

  • ✅ Eliminates code duplication across packages
  • ✅ Ensures error modals work in all contexts (topology, standalone pages, import flows)
  • ✅ Single source of truth for error modal launching
  • ✅ Proper initialization and cleanup via React hooks

Test plan

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered

Related

Jira: CONSOLE-5012

🤖 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.

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

openshift-ci-robot commented Jan 23, 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

Creates a shared global error modal launcher utility in the console-shared package to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Note: This PR is based on CONSOLE-5012 and includes changes from other PRs in that branch. The changes below are cumulative and may include work from multiple related PRs.

Changes

New Shared Utility

  • packages/console-shared/src/utils/error-modal-handler.ts
  • launchGlobalErrorModal() - Launch error modals from non-React contexts
  • useSetupGlobalErrorModalLauncher() - Hook to initialize the launcher
  • setGlobalErrorModalLauncher() - Manage global state

Migrated Packages

  • topology: Updated Topology.tsx, componentUtils.ts, edgeActions.ts
  • knative-plugin: Updated knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: Updated logs-utils.ts, BuildRunDetailsPage.tsx
  • dev-console: Updated pipeline-template-utils.ts, ImportPage.tsx, DeployImagePage.tsx

Launcher Initialization

Added useSetupGlobalErrorModalLauncher() in root components:

  • packages/topology/src/components/graph-view/Topology.tsx - For topology drag/drop contexts
  • packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx - For standalone log viewing
  • packages/dev-console/src/components/import/ImportPage.tsx - For Git import flow
  • packages/dev-console/src/components/import/DeployImagePage.tsx - For deploy image flow

Cleanup

  • Removed topology-specific errorModalHandler implementation
  • Removed deprecated errorModal function from public/components/modals

Benefits

  • ✅ Eliminates code duplication across packages
  • ✅ Ensures error modals work in all contexts (topology, standalone pages, import flows)
  • ✅ Single source of truth for error modal launching
  • ✅ Proper initialization and cleanup via React hooks

Test plan

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered

Related

Jira: CONSOLE-5012

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

openshift-ci-robot commented Jan 23, 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

Creates a shared global error modal launcher utility in the console-shared package to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Note: This PR builds on:

Changes

New Shared Utility

  • packages/console-shared/src/utils/error-modal-handler.ts
  • launchGlobalErrorModal() - Launch error modals from non-React contexts
  • useSetupGlobalErrorModalLauncher() - Hook to initialize the launcher
  • setGlobalErrorModalLauncher() - Manage global state

Migrated Packages

  • topology: Updated Topology.tsx, componentUtils.ts, edgeActions.ts
  • knative-plugin: Updated knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: Updated logs-utils.ts, BuildRunDetailsPage.tsx
  • dev-console: Updated pipeline-template-utils.ts, ImportPage.tsx, DeployImagePage.tsx

Launcher Initialization

Added useSetupGlobalErrorModalLauncher() in root components:

  • packages/topology/src/components/graph-view/Topology.tsx - For topology drag/drop contexts
  • packages/shipwright-plugin/src/components/buildrun-details/BuildRunDetailsPage.tsx - For standalone log viewing
  • packages/dev-console/src/components/import/ImportPage.tsx - For Git import flow
  • packages/dev-console/src/components/import/DeployImagePage.tsx - For deploy image flow

Cleanup

  • Removed topology-specific errorModalHandler implementation
  • Removed deprecated errorModal function from public/components/modals

Benefits

  • ✅ Eliminates code duplication across packages
  • ✅ Ensures error modals work in all contexts (topology, standalone pages, import flows)
  • ✅ Single source of truth for error modal launching
  • ✅ Proper initialization and cleanup via React hooks

Test plan

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered

Related

Jira: CONSOLE-5012

🤖 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 added the kind/i18n Indicates issue or PR relates to internationalization or has content that needs to be translated label Jan 23, 2026
rhamilto added a commit to rhamilto/console that referenced this pull request Jan 23, 2026
This commit migrates all instances of confirmModal to use the modern
overlay pattern with useWarningModal, completing the modal migration
effort started in previous PRs.

Changes:
- Removed deprecated confirmModal launcher and confirm-modal.tsx file
- Migrated topology moveNodeToGroup to use useWarningModal with proper
  cancel handling via global handler pattern
- Updated StopNodeMaintenanceModal to remove deprecated imperative API
  and keep only the useStopNodeMaintenanceModal hook
- Fixed useWarningModal to properly call closeOverlay() and filter it
  from props spread to prevent React DOM warnings
- Added try-catch in topology drag-drop to prevent optimistic update
  when user cancels the move operation
- Updated all metal3-plugin maintenance components to use the hook-based
  approach

This PR builds upon and depends on:
- openshift#15932 (ResourceLimitsModal migration)
- openshift#15939 (DeletePDBModal migration)
- openshift#15940 (ConfigureUnschedulableModal migration)
- openshift#15946 (Global error modal utility)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@rhamilto
Copy link
Member Author

/retest

2 similar comments
@rhamilto
Copy link
Member Author

/retest

@rhamilto
Copy link
Member Author

/retest

rhamilto added a commit to rhamilto/console that referenced this pull request Jan 26, 2026
This commit migrates all instances of confirmModal to use the modern
overlay pattern with useWarningModal, completing the modal migration
effort started in previous PRs.

Changes:
- Removed deprecated confirmModal launcher and confirm-modal.tsx file
- Migrated topology moveNodeToGroup to use useWarningModal with proper
  cancel handling via global handler pattern
- Updated StopNodeMaintenanceModal to remove deprecated imperative API
  and keep only the useStopNodeMaintenanceModal hook
- Fixed useWarningModal to properly call closeOverlay() and filter it
  from props spread to prevent React DOM warnings
- Added try-catch in topology drag-drop to prevent optimistic update
  when user cancels the move operation
- Updated all metal3-plugin maintenance components to use the hook-based
  approach

This PR builds upon and depends on:
- openshift#15932 (ResourceLimitsModal migration)
- openshift#15939 (DeletePDBModal migration)
- openshift#15940 (ConfigureUnschedulableModal migration)
- openshift#15946 (Global error modal utility)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@rhamilto
Copy link
Member Author

/retest

rhamilto added a commit to rhamilto/console that referenced this pull request Feb 3, 2026
…veNodeToGroup

This commit completes the migration of confirmModal to the modern overlay
pattern and refactors moveNodeToGroup to use React Context, eliminating
global state race conditions and improving testability.

## Changes

### Removed deprecated confirmModal
- Deleted confirm-modal.tsx and its imperative launcher
- Removed confirmModal from modals/index.ts exports
- Removed translation key from public.json

### Migrated to useWarningModal overlay pattern
- Updated topology moveNodeToGroup to use useWarningModal with proper cancel handling
- Updated StopNodeMaintenanceModal to remove imperative API, keeping only hook-based approach
- Updated all metal3-plugin maintenance components to use hook-based approach
- Fixed useWarningModal to properly call closeOverlay() and filter it from props spread

### Refactored moveNodeToGroup to use React Context
- Created MoveNodeHandlersProvider using React Context pattern
- Created SyncMoveNodeHandlers component to sync handlers for non-React contexts
- Added useMoveNodeHandlers() hook for accessing handlers
- Wrapped Topology component with MoveNodeHandlersProvider
- Deprecated old useSetupMoveNodeToGroupHandlers hooks with warnings
- Added comprehensive unit tests (8 tests covering all scenarios)

### Fixed accessibility violation
- Blur active element before launching confirmation modal
- Prevents focus from remaining on SVG elements in topology graph
- Fixes browser warning: "Blocked aria-hidden on an element because its descendant retained focus"

## Benefits
- Eliminates global state race conditions
- Follows React Context pattern established in openshift#15946
- Improves testability with mockable context
- Single provider initialization around Topology component
- Proper cleanup via React lifecycle
- Type-safe with clear error messages
- Maintains backward compatibility via deprecated hooks
- Resolves accessibility violations with aria-hidden elements

The hybrid approach (Context + module reference) supports both React components
(via useMoveNodeHandlers hook) and non-React drag-drop callbacks (via moveNodeToGroup
function), consistent with the error modal handler pattern.

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

openshift-ci-robot commented Feb 3, 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

Creates a shared error modal launcher utility in the console-shared package using React Context to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Architecture

React Context Pattern

This implementation reuses the existing OverlayProvider instead of creating a new context provider:

<OverlayProvider>
 <SyncErrorModalLauncher />  {/* Zero-render sync component */}
 <App />
</OverlayProvider>

Dual API for Different Contexts

React Components - Use the hook:

const launchErrorModal = useErrorModalLauncher();
launchErrorModal({ title: 'Error', error: err.message });

Non-React Code (callbacks, promises, utilities) - Use the function:

createConnection(source, target).catch(err => {
 launchErrorModal({ title: 'Failed', error: err.message });
});

Changes

New Shared Utility

packages/console-shared/src/utils/error-modal-handler.tsx

  • SyncErrorModalLauncher - Component that syncs the launcher for non-React contexts
  • useErrorModalLauncher() - Hook for launching error modals from React components
  • launchErrorModal() - Function for launching from non-React contexts (callbacks, promises)

Test Coverage

packages/console-shared/src/utils/__tests__/error-modal-handler.spec.tsx

  • Unit tests covering all scenarios (5 tests, all passing)
  • Tests lifecycle (mount/unmount)
  • Tests error handling (uninitialized launcher)

packages/console-shared/src/utils/__mocks__/error-modal-handler.tsx

  • Mock utilities for testing components that use the error modal launcher

Single Initialization Point

public/components/app.tsx

  • Added <SyncErrorModalLauncher /> inside OverlayProvider
  • Single initialization point for the entire application

Topology Move Node Error Handling

packages/topology/src/utils/moveNodeToGroup.tsx

  • Added setMoveNodeToGroupErrorHandler() for managing error handler
  • Added useSetupMoveNodeToGroupErrorHandler() hook called in Topology component
  • Updated moveNodeToGroup() to accept optional onError callback
  • Deprecated useMoveNodeToGroup() hook

packages/topology/src/components/graph-view/Topology.tsx

  • Added useSetupMoveNodeToGroupErrorHandler() call to initialize error handling

Note: This uses a global handler pattern instead of React Context. A follow-up PR (#15948) will refactor this to use the same React Context pattern as the error modal handler.

Migrated to New API

Updated to use launchErrorModal in:

  • topology: componentUtils.ts, edgeActions.ts
  • knative-plugin: knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: logs-utils.ts
  • dev-console: pipeline-template-utils.ts

Cleanup

  • Removed deprecated errorModal function from public/components/modals
  • Updated exports in console-shared package

Benefits

  • Eliminates global state race conditions - Single initialization point in app root
  • Improves testability - OverlayProvider context can be mocked in tests
  • No code duplication - Shared utility used across all packages
  • Reuses existing infrastructure - No new providers, uses existing OverlayProvider
  • Simpler architecture - No unnecessary provider nesting
  • Type-safe - Full TypeScript support with proper generics
  • Proper lifecycle - Cleanup on unmount via useEffect
  • Backward compatible - Non-React code continues to work via module reference
  • Comprehensive test coverage - 5 unit tests covering all scenarios

Test Plan

Automated Tests

  • ✅ Unit tests pass (5/5 tests)
  • ✅ TypeScript compilation succeeds
  • ✅ ESLint passes
  • ✅ Pre-commit hooks pass

Manual Testing

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered
  • Test error modal from React component using useErrorModalLauncher() hook
  • Test error modal from promise chain using launchErrorModal() function
  • Test topology node move operations with error handling

Technical Details

Hybrid Approach

The implementation uses a hybrid pattern to support both React and non-React code:

  1. React Components: Call useErrorModalLauncher() hook which directly uses useOverlay()
  2. Non-React Code: Call launchErrorModal() function which accesses a module-level reference
  3. Sync Component: SyncErrorModalLauncher syncs the launcher to the module-level variable

This approach:

  • Eliminates the need for a custom Context provider
  • Reuses the existing OverlayProvider infrastructure
  • Supports both React hooks and imperative API calls
  • Has proper cleanup via React lifecycle

Migration Path

Before:

  • No shared utility
  • Each package implemented its own error modal handling
  • Inconsistent patterns across codebase
  • Direct imports of deprecated errorModal function

After:

  • Single initialization point (app.tsx)
  • React Context-based (via OverlayProvider)
  • Proper cleanup on unmount
  • Easy to test with mocks
  • Consistent API across all packages

Related

🤖 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.

@rhamilto
Copy link
Member Author

rhamilto commented Feb 3, 2026

/retest

let moduleErrorModalLauncher: ((props: ErrorModalProps) => void) | null = null;

/**
* Component that syncs the error modal launcher to module-level for non-React contexts.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we expect to eventually remove all uses of error modal which are outside of React render flow?

i.e. do we have lots of cases where the error modal is used outside of a React component?

Copy link
Member Author

Choose a reason for hiding this comment

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

Claude says

# Error Modal Non-React Usage Report

## Summary

This report documents all locations where error modals are used outside React contexts in the OpenShift Console codebase. These usages justify the hybrid approach with both `useErrorModalLauncher()` hook (for React components) and `launchErrorModal()` function (for non-React contexts).

**Total: 10 non-React usage locations across 7 files**

## Detailed Breakdown

### 1. Topology - Move Node to Group (2 usages)

**Files:**
- `packages/topology/src/utils/moveNodeToGroup.tsx:62`
- `packages/topology/src/utils/moveNodeToGroup.tsx:76`

**Context:**
- Used in `.catch()` callbacks within promise chains
- Called from drag-and-drop `end` event handler (non-React context)
- Executes when moving nodes between application groups fails

**Code Example:**
```typescript
updateTopologyResourceApplication(node, targetGroup?.getLabel())
  .catch((err) => {
    const error = err.message;
    if (onError) {
      onError(error);
    } else {
      launchErrorModal({ error });
    }
  });

Why Non-React Context?

  • Callback from PatternFly Topology drag source spec
  • Executes outside React render cycle
  • No access to hooks

2. Topology - Create Connection

File: packages/topology/src/components/graph-view/components/componentUtils.ts:350

Context:

  • Used in .catch() callback when creating visual connectors
  • Called from createVisualConnector() utility function
  • Triggered during drag-and-drop connection creation

Code Example:

createConnection(source, target, null).catch((error) => {
  launchErrorModal({
    title: i18next.t('topology~Error creating connection'),
    error: error.message,
  });
});

Why Non-React Context?

  • Called from drag-and-drop callback
  • Utility function, not a React component
  • Asynchronous promise chain

3. Topology - Move Edge Connection

File: packages/topology/src/components/graph-view/components/componentUtils.ts:313

Context:

  • Used in edge drag source spec end handler
  • Executes when moving connector edges between nodes

Code Example:

callback(edge.getSource(), dropResult, edge.getTarget()).catch((error) => {
  launchErrorModal({ title, error: error.message });
});

Why Non-React Context?

  • Drag source spec callback (imperative API)
  • Outside React component lifecycle
  • No hook access

4. Topology - Delete Edge

File: packages/topology/src/actions/edgeActions.ts:106

Context:

  • Used in confirmation modal's onConfirm callback
  • Executes after user confirms edge deletion
  • Promise chain error handler

Code Example:

onConfirm: () => {
  return removeTopologyResourceConnection(element, resource).catch((err) => {
    if (err) {
      launchErrorModal({
        title: t('topology~Error deleting connector'),
        error: err.message,
      });
    }
  });
}

Why Non-React Context?

  • Modal callback (outside normal React flow)
  • Asynchronous operation
  • Error handling in promise chain

5. Knative - Event Source Sink Connection

File: packages/knative-plugin/src/topology/components/knativeComponentUtils.ts:191

Context:

  • Edge drag source spec for event sources
  • Moves event source sink connections

Code Example:

createSinkConnection(edge.getSource(), dropResult).catch((error) => {
  launchErrorModal({
    title: i18next.t('knative-plugin~Error moving event source sink'),
    error: error.message,
  });
});

Why Non-React Context?

  • Drag source end handler
  • Imperative callback API
  • No React context available

6. Knative - Kafka Connection (Drag)

File: packages/knative-plugin/src/topology/components/knativeComponentUtils.ts:224

Context:

  • Edge drag source spec for Kafka connections
  • Handles moving Kafka connector edges

Code Example:

createEventSourceKafkaConnection(edge.getSource(), dropResult).catch((error) => {
  launchErrorModal({
    title: i18next.t('knative-plugin~Error moving event source kafka connector'),
    error: error?.message,
  });
});

Why Non-React Context?

  • Drag source callback
  • Outside React render
  • Asynchronous operation

7. Knative - PubSub Connection

File: packages/knative-plugin/src/topology/components/knativeComponentUtils.ts:261

Context:

  • Edge drag source spec for PubSub connections
  • Error handling for sink PubSub connections

Code Example:

createSinkPubSubConnection(edge, dropResult).catch((error) => {
  launchErrorModal({
    title: i18next.t('knative-plugin~Error while sink'),
    error: error.message,
  });
});

Why Non-React Context?

  • Drag source spec callback
  • Imperative API
  • No hook support

8. Knative - Create Kafka Connection (Utility)

File: packages/knative-plugin/src/topology/create-connector-utils.ts:18

Context:

  • Utility function for creating Kafka connections
  • Called from topology connector creation flow

Code Example:

const createKafkaConnection = (source: Node, target: Node) =>
  createEventSourceKafkaConnection(source, target)
    .then(() => null)
    .catch((error) => {
      launchErrorModal({
        title: i18next.t('knative-plugin~Error moving event source kafka connector'),
        error: error.message,
      });
    });

Why Non-React Context?

  • Pure utility function
  • Called from multiple contexts
  • Cannot use React hooks

9. Shipwright - Download Logs

File: packages/shipwright-plugin/src/components/logs/logs-utils.ts:84

Context:

  • Async utility function for downloading build run logs
  • Called by React components but error handling is in utility layer

Code Example:

export const getBuildRunPodLogs = (buildRun: TaskRunKind): Promise<ContainerLogOptions[]> => {
  return k8sGet<PodKind>(PodModel, podName, ns)
    .then((pod) => {
      return /* process logs */;
    })
    .catch((err) => {
      launchErrorModal({
        error: err.message || i18next.t('shipwright-plugin~Error downloading logs.'),
      });
      return [];
    });
};

Why Non-React Context?

  • Async utility function
  • Promise chain error handler
  • Shared by multiple components

10. Dev Console - Expose Route

File: packages/dev-console/src/components/pipeline-section/pipeline/pipeline-template-utils.ts:931

Context:

  • Async utility for exposing webhook routes
  • Error handling in try/catch block

Code Example:

export const exposeWebhookRoute = async (/* params */) => {
  try {
    const route = createRoute(/* params */);
    await k8sCreate(RouteModel, route, { ns });
  } catch (e) {
    launchErrorModal({
      title: 'Error Exposing Route',
      error: e.message || 'Unknown error exposing the Webhook route',
    });
  }
};

Why Non-React Context?

  • Async utility function
  • Try/catch error handling
  • Called from multiple contexts

Usage Categories

By Type

Category Count Percentage
Drag/Drop Operations 7 70%
Async Utility Functions 2 20%
User-Triggered Actions 1 10%
Total 10 100%

By Package

Package Count Files
topology 4 3 files
knative-plugin 4 2 files
shipwright-plugin 1 1 file
dev-console 1 1 file
Total 10 7 files

Can These Be Refactored to React Contexts?

Short Answer: No, most cannot be easily refactored.

Detailed Analysis

Drag/Drop Operations (7/10 usages)

  • Cannot refactor - These are event handlers from PatternFly's topology library
  • Execute outside React's render cycle
  • Callbacks in imperative third-party APIs
  • No access to React hooks or context

Async Utility Functions (2/10 usages)

  • Should not refactor - These are pure utility functions
  • Called from multiple contexts (some React, some not)
  • Maintaining as utilities provides better separation of concerns
  • Would require passing error handlers through multiple layers

Modal Callbacks (1/10 usage)

  • ⚠️ Difficult to refactor - Confirmation modal callbacks
  • Execute outside normal React flow
  • Would require restructuring modal architecture

Why the Hybrid Approach is Necessary

The codebase requires both patterns:

useErrorModalLauncher() Hook (React Context)

✅ Use in React components
✅ Direct access to useOverlay()
✅ Type-safe, idiomatic React

launchErrorModal() Function (Module-Level)

✅ Use in imperative callbacks
✅ Use in async utility functions
✅ Use in third-party library callbacks
✅ Use in promise chains

Architecture Benefits

  1. Single Source of Truth: Both APIs use the same underlying OverlayProvider
  2. Proper Lifecycle: SyncErrorModalLauncher manages initialization/cleanup
  3. No Duplication: Shared implementation across all contexts
  4. Type Safety: Both APIs use the same ErrorModalProps interface
  5. Testable: Mock implementation supports both patterns

Conclusion

The current implementation correctly addresses a real architectural need:

  • 10 locations require non-React error modal launching
  • 70% are drag/drop operations (imperative callbacks)
  • Cannot be easily refactored to pure React patterns without major architectural changes

The hybrid approach with SyncErrorModalLauncher + module-level reference is the appropriate solution for this codebase's requirements.


Related

  • PR: #15946
  • Jira: CONSOLE-5012
  • Implementation: packages/console-shared/src/utils/error-modal-handler.tsx

Report generated: 2026-02-03

@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 3, 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

Creates a shared error modal launcher utility in the console-shared package using React Context to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Architecture

React Context Pattern

This implementation reuses the existing OverlayProvider instead of creating a new context provider:

<OverlayProvider>
 <SyncErrorModalLauncher />  {/* Zero-render sync component */}
 <App />
</OverlayProvider>

Dual API for Different Contexts

React Components - Use the hook:

const launchErrorModal = useErrorModalLauncher();
launchErrorModal({ title: 'Error', error: err.message });

Non-React Code (callbacks, promises, utilities) - Use the function:

createConnection(source, target).catch(err => {
 launchErrorModal({ title: 'Failed', error: err.message });
});

Changes

New Shared Utility

packages/console-shared/src/utils/error-modal-handler.tsx

  • SyncErrorModalLauncher - Component that syncs the launcher for non-React contexts
  • useErrorModalLauncher() - Hook for launching error modals from React components
  • launchErrorModal() - Function for launching from non-React contexts (callbacks, promises)

Test Coverage

packages/console-shared/src/utils/__tests__/error-modal-handler.spec.tsx

  • Unit tests covering all scenarios (5 tests, all passing)
  • Tests lifecycle (mount/unmount)
  • Tests error handling (uninitialized launcher)

packages/console-shared/src/utils/__mocks__/error-modal-handler.tsx

  • Mock utilities for testing components that use the error modal launcher

Single Initialization Point

public/components/app.tsx

  • Added <SyncErrorModalLauncher /> inside OverlayProvider
  • Single initialization point for the entire application

Migrated to Shared Pattern

packages/topology/src/utils/moveNodeToGroup.tsx

  • Migrated from custom globalErrorHandler to shared launchErrorModal()
  • Removed setMoveNodeToGroupErrorHandler() and useSetupMoveNodeToGroupErrorHandler()
  • Removed deprecated useMoveNodeToGroup() hook
  • Now uses consistent pattern with rest of codebase

packages/topology/src/components/graph-view/Topology.tsx

  • Removed useSetupMoveNodeToGroupErrorHandler() initialization (no longer needed)

Migrated to New API

Updated to use launchErrorModal in:

  • topology: componentUtils.ts, edgeActions.ts, moveNodeToGroup.tsx
  • knative-plugin: knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: logs-utils.ts
  • dev-console: pipeline-template-utils.ts

Cleanup

  • Removed deprecated errorModal function from public/components/modals
  • Removed redundant resetErrorModalMocks() helper from mock file
  • Updated exports in console-shared package

Benefits

  • Eliminates global state race conditions - Single initialization point in app root
  • Consistent pattern across codebase - All error modals use the same shared utility
  • Improves testability - OverlayProvider context can be mocked in tests
  • No code duplication - Shared utility used across all packages
  • Reuses existing infrastructure - No new providers, uses existing OverlayProvider
  • Simpler architecture - No unnecessary provider nesting or custom error handlers
  • Type-safe - Full TypeScript support with proper generics
  • Proper lifecycle - Cleanup on unmount via useEffect
  • Backward compatible - Non-React code continues to work via module reference
  • Comprehensive test coverage - 5 unit tests covering all scenarios

Test Plan

Automated Tests

  • ✅ Unit tests pass (5/5 tests)
  • ✅ TypeScript compilation succeeds
  • ✅ ESLint passes
  • ✅ Pre-commit hooks pass

Manual Testing

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered
  • Test error modal from React component using useErrorModalLauncher() hook
  • Test error modal from promise chain using launchErrorModal() function
  • Test topology node move operations with error handling

Technical Details

Hybrid Approach

The implementation uses a hybrid pattern to support both React and non-React code:

  1. React Components: Call useErrorModalLauncher() hook which directly uses useOverlay()
  2. Non-React Code: Call launchErrorModal() function which accesses a module-level reference
  3. Sync Component: SyncErrorModalLauncher syncs the launcher to the module-level variable

This approach:

  • Eliminates the need for a custom Context provider
  • Reuses the existing OverlayProvider infrastructure
  • Supports both React hooks and imperative API calls
  • Has proper cleanup via React lifecycle

Migration Path

Before:

  • No shared utility
  • Each package implemented its own error modal handling
  • Inconsistent patterns across codebase (custom global handlers, direct imports)
  • Direct imports of deprecated errorModal function

After:

  • Single initialization point (app.tsx)
  • React Context-based (via OverlayProvider)
  • Proper cleanup on unmount
  • Easy to test with mocks
  • Consistent API across all packages
  • No custom error handler patterns

Related

🤖 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.

@rhamilto rhamilto changed the title [WIP] CONSOLE-5012: Create shared error modal launcher using React Context CONSOLE-5012: Create shared error modal launcher using React Context Feb 3, 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 Feb 3, 2026
rhamilto added a commit to rhamilto/console that referenced this pull request Feb 3, 2026
…veNodeToGroup

This commit completes the migration of confirmModal to the modern overlay
pattern and refactors moveNodeToGroup to use React Context, eliminating
global state race conditions and improving testability.

- Deleted confirm-modal.tsx and its imperative launcher
- Removed confirmModal from modals/index.ts exports
- Removed translation key from public.json

- Updated topology moveNodeToGroup to use useWarningModal with proper cancel handling
- Updated StopNodeMaintenanceModal to remove imperative API, keeping only hook-based approach
- Updated all metal3-plugin maintenance components to use hook-based approach
- Fixed useWarningModal to properly call closeOverlay() and filter it from props spread

- Created MoveNodeHandlersProvider using React Context pattern
- Created SyncMoveNodeHandlers component to sync handlers for non-React contexts
- Added useMoveNodeHandlers() hook for accessing handlers
- Wrapped Topology component with MoveNodeHandlersProvider
- Deprecated old useSetupMoveNodeToGroupHandlers hooks with warnings
- Added comprehensive unit tests (8 tests covering all scenarios)

- Blur active element before launching confirmation modal
- Prevents focus from remaining on SVG elements in topology graph
- Fixes browser warning: "Blocked aria-hidden on an element because its descendant retained focus"

- Eliminates global state race conditions
- Follows React Context pattern established in openshift#15946
- Improves testability with mockable context
- Single provider initialization around Topology component
- Proper cleanup via React lifecycle
- Type-safe with clear error messages
- Maintains backward compatibility via deprecated hooks
- Resolves accessibility violations with aria-hidden elements

The hybrid approach (Context + module reference) supports both React components
(via useMoveNodeHandlers hook) and non-React drag-drop callbacks (via moveNodeToGroup
function), consistent with the error modal handler pattern.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
rhamilto added a commit to rhamilto/console that referenced this pull request Feb 3, 2026
…veNodeToGroup

This commit completes the migration of confirmModal to the modern overlay
pattern and refactors moveNodeToGroup to use React Context, eliminating
global state race conditions and improving testability.

- Deleted confirm-modal.tsx and its imperative launcher
- Removed confirmModal from modals/index.ts exports
- Removed translation key from public.json

- Updated topology moveNodeToGroup to use useWarningModal with proper cancel handling
- Updated StopNodeMaintenanceModal to remove imperative API, keeping only hook-based approach
- Updated all metal3-plugin maintenance components to use hook-based approach
- Fixed useWarningModal to properly call closeOverlay() and filter it from props spread

- Created MoveNodeHandlersProvider using React Context pattern
- Created SyncMoveNodeHandlers component to sync handlers for non-React contexts
- Added useMoveNodeHandlers() hook for accessing handlers
- Wrapped Topology component with MoveNodeHandlersProvider
- Deprecated old useSetupMoveNodeToGroupHandlers hooks with warnings
- Added comprehensive unit tests (8 tests covering all scenarios)

- Blur active element before launching confirmation modal
- Prevents focus from remaining on SVG elements in topology graph
- Fixes browser warning: "Blocked aria-hidden on an element because its descendant retained focus"

- Eliminates global state race conditions
- Follows React Context pattern established in openshift#15946
- Improves testability with mockable context
- Single provider initialization around Topology component
- Proper cleanup via React lifecycle
- Type-safe with clear error messages
- Maintains backward compatibility via deprecated hooks
- Resolves accessibility violations with aria-hidden elements

The hybrid approach (Context + module reference) supports both React components
(via useMoveNodeHandlers hook) and non-React drag-drop callbacks (via moveNodeToGroup
function), consistent with the error modal handler pattern.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
rhamilto added a commit to rhamilto/console that referenced this pull request Feb 3, 2026
…veNodeToGroup

This commit completes the migration of confirmModal to the modern overlay
pattern and refactors moveNodeToGroup to use React Context, eliminating
global state race conditions and improving testability.

- Deleted confirm-modal.tsx and its imperative launcher
- Removed confirmModal from modals/index.ts exports
- Removed translation key from public.json

- Updated topology moveNodeToGroup to use useWarningModal with proper cancel handling
- Updated StopNodeMaintenanceModal to remove imperative API, keeping only hook-based approach
- Updated all metal3-plugin maintenance components to use hook-based approach
- Fixed useWarningModal to properly call closeOverlay() and filter it from props spread

- Created MoveNodeHandlersProvider using React Context pattern
- Created SyncMoveNodeHandlers component to sync handlers for non-React contexts
- Added useMoveNodeHandlers() hook for accessing handlers
- Wrapped Topology component with MoveNodeHandlersProvider
- Deprecated old useSetupMoveNodeToGroupHandlers hooks with warnings
- Added comprehensive unit tests (8 tests covering all scenarios)

- Blur active element before launching confirmation modal
- Prevents focus from remaining on SVG elements in topology graph
- Fixes browser warning: "Blocked aria-hidden on an element because its descendant retained focus"

- Eliminates global state race conditions
- Follows React Context pattern established in openshift#15946
- Improves testability with mockable context
- Single provider initialization around Topology component
- Proper cleanup via React lifecycle
- Type-safe with clear error messages
- Maintains backward compatibility via deprecated hooks
- Resolves accessibility violations with aria-hidden elements

The hybrid approach (Context + module reference) supports both React components
(via useMoveNodeHandlers hook) and non-React drag-drop callbacks (via moveNodeToGroup
function), consistent with the error modal handler pattern.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@rhamilto
Copy link
Member Author

rhamilto commented Feb 4, 2026

/retest

@vojtechszocs
Copy link
Contributor

/lgtm

@rhamilto
Copy link
Member Author

rhamilto commented Feb 4, 2026

@yanpzhan, this is ready for verification when you are.

@openshift-ci openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Feb 4, 2026
- Add error-modal-handler.tsx to console-shared package with:
  - SyncErrorModalLauncher - Zero-render component that syncs launcher for non-React contexts
  - useErrorModalLauncher() - Hook for launching error modals from React components
  - launchErrorModal() - Function for launching from non-React contexts (callbacks, promises)

- Add SyncErrorModalLauncher to app.tsx inside OverlayProvider
  - Single initialization point for the entire application
  - Reuses existing OverlayProvider instead of creating new context

- Migrate packages to use launchErrorModal:
  - topology: componentUtils.ts, edgeActions.ts
  - knative-plugin: knativeComponentUtils.ts, create-connector-utils.ts
  - shipwright-plugin: logs-utils.ts
  - dev-console: pipeline-template-utils.ts

- Update moveNodeToGroup.tsx to support error handling:
  - Add setMoveNodeToGroupErrorHandler() for managing error handler
  - Add useSetupMoveNodeToGroupErrorHandler() hook for Topology component
  - Update moveNodeToGroup() to accept optional onError callback
  - Deprecate useMoveNodeToGroup() hook

- Add comprehensive test coverage:
  - error-modal-handler.spec.tsx - 5 unit tests covering all scenarios
  - __mocks__/error-modal-handler.tsx - Mock utilities for testing

- Remove deprecated errorModal() function from public/components/modals

This establishes a React Context-based pattern for error modals that
eliminates global state race conditions, improves testability, and
provides a consistent API across all packages. The hybrid approach
supports both React hooks and imperative API calls via module-level
reference with proper cleanup via React lifecycle.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@openshift-ci openshift-ci bot removed the lgtm Indicates that a PR is ready to be merged. label Feb 4, 2026
@logonoff
Copy link
Member

logonoff commented Feb 4, 2026

/lgtm

@openshift-ci
Copy link
Contributor

openshift-ci bot commented Feb 4, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: logonoff, rhamilto, vojtechszocs

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 openshift-ci bot added the lgtm Indicates that a PR is ready to be merged. label Feb 4, 2026
@openshift-ci-robot
Copy link
Contributor

openshift-ci-robot commented Feb 4, 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

Creates a shared error modal launcher utility in the console-shared package using React Context to eliminate code duplication and ensure error modals work correctly across all contexts (topology, standalone pages, import flows).

Architecture

React Context Pattern

This implementation reuses the existing OverlayProvider instead of creating a new context provider:

<OverlayProvider>
 <SyncErrorModalLauncher />  {/* Zero-render sync component */}
 <App />
</OverlayProvider>

Dual API for Different Contexts

React Components - Use the hook:

const launchErrorModal = useErrorModalLauncher();
launchErrorModal({ title: 'Error', error: err.message });

Non-React Code (callbacks, promises, utilities) - Use the function:

createConnection(source, target).catch(err => {
 launchErrorModal({ title: 'Failed', error: err.message });
});

Changes

New Shared Utility

packages/console-shared/src/utils/error-modal-handler.tsx

  • SyncErrorModalLauncher - Component that syncs the launcher for non-React contexts
  • useErrorModalLauncher() - Hook for launching error modals from React components
  • launchErrorModal() - Function for launching from non-React contexts (callbacks, promises)

Test Coverage

packages/console-shared/src/utils/__tests__/error-modal-handler.spec.tsx

  • Unit tests covering all scenarios (5 tests, all passing)
  • Tests lifecycle (mount/unmount)
  • Tests error handling (uninitialized launcher)

packages/console-shared/src/utils/__mocks__/error-modal-handler.tsx

  • Mock utilities for testing components that use the error modal launcher

Single Initialization Point

public/components/app.tsx

  • Added <SyncErrorModalLauncher /> inside OverlayProvider
  • Single initialization point for the entire application

Migrated to Shared Pattern

packages/topology/src/utils/moveNodeToGroup.tsx

  • Migrated from custom globalErrorHandler to shared launchErrorModal()
  • Removed setMoveNodeToGroupErrorHandler() and useSetupMoveNodeToGroupErrorHandler()
  • Removed deprecated useMoveNodeToGroup() hook
  • Now uses consistent pattern with rest of codebase

packages/topology/src/components/graph-view/Topology.tsx

  • Removed useSetupMoveNodeToGroupErrorHandler() initialization (no longer needed)

Migrated to New API

Updated to use launchErrorModal in:

  • topology: componentUtils.ts, edgeActions.ts, moveNodeToGroup.tsx
  • knative-plugin: knativeComponentUtils.ts, create-connector-utils.ts
  • shipwright-plugin: logs-utils.ts
  • dev-console: pipeline-template-utils.ts

Cleanup

  • Removed deprecated errorModal function from public/components/modals
  • Removed redundant resetErrorModalMocks() helper from mock file
  • Updated exports in console-shared package

Benefits

  • Eliminates global state race conditions - Single initialization point in app root
  • Consistent pattern across codebase - All error modals use the same shared utility
  • Improves testability - OverlayProvider context can be mocked in tests
  • No code duplication - Shared utility used across all packages
  • Reuses existing infrastructure - No new providers, uses existing OverlayProvider
  • Simpler architecture - No unnecessary provider nesting or custom error handlers
  • Type-safe - Full TypeScript support with proper generics
  • Proper lifecycle - Cleanup on unmount via useEffect
  • Backward compatible - Non-React code continues to work via module reference
  • Comprehensive test coverage - 5 unit tests covering all scenarios

Test Plan

Automated Tests

  • ✅ Unit tests pass (5/5 tests)
  • ✅ TypeScript compilation succeeds
  • ✅ ESLint passes
  • ✅ Pre-commit hooks pass

Manual Testing

  • Test topology drag/drop operations that trigger errors
  • Test BuildRun log viewing with error conditions
  • Test Git import flow with route exposure errors
  • Test deploy image flow with pipeline trigger errors
  • Verify no console errors when error modals are triggered
  • Test error modal from React component using useErrorModalLauncher() hook
  • Test error modal from promise chain using launchErrorModal() function
  • Test topology node move operations with error handling

Technical Details

Hybrid Approach

The implementation uses a hybrid pattern to support both React and non-React code:

  1. React Components: Call useErrorModalLauncher() hook which directly uses useOverlay()
  2. Non-React Code: Call launchErrorModal() function which accesses a module-level reference
  3. Sync Component: SyncErrorModalLauncher syncs the launcher to the module-level variable

This approach:

  • Eliminates the need for a custom Context provider
  • Reuses the existing OverlayProvider infrastructure
  • Supports both React hooks and imperative API calls
  • Has proper cleanup via React lifecycle

Migration Path

Before:

  • No shared utility
  • Each package implemented its own error modal handling
  • Inconsistent patterns across codebase (custom global handlers, direct imports)
  • Direct imports of deprecated errorModal function

After:

  • Single initialization point (app.tsx)
  • React Context-based (via OverlayProvider)
  • Proper cleanup on unmount
  • Easy to test with mocks
  • Consistent API across all packages
  • No custom error handler patterns

Related

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

  • Added improved error modal handling system with enhanced initialization and non-React context support

  • Added new error message for connector deletion workflows

  • Bug Fixes

  • Enhanced error handling in drag-and-drop and routing operations to use centralized launcher

  • Tests

  • Added comprehensive test coverage for new error modal utilities and handlers

  • Refactor

  • Migrated error modal usage across multiple components to use new centralized launcher

  • Removed deprecated error modal implementation in favor of new handler

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 4, 2026

📝 Walkthrough

Walkthrough

This change introduces a new centralized error modal handler utility providing both React and non-React interfaces for error modal launching. A new SyncErrorModalLauncher React component initializes the handler within the app's provider tree, while useErrorModalLauncher hook and launchErrorModal function accommodate React and non-React contexts respectively. The deprecated errorModal launcher is removed from the public API, and all existing call sites across multiple packages migrate to the new launchErrorModal function. The moveNodeToGroup utility gains an optional onError callback for custom error handling. New test coverage and mock implementations support the refactoring.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main objective: introducing a shared error modal launcher utility leveraging React Context for centralized modal handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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

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

rhamilto added a commit to rhamilto/console that referenced this pull request Feb 4, 2026
…veNodeToGroup

This commit completes the migration of confirmModal to the modern overlay
pattern and refactors moveNodeToGroup to use React Context, eliminating
global state race conditions and improving testability.

- Deleted confirm-modal.tsx and its imperative launcher
- Removed confirmModal from modals/index.ts exports
- Removed translation key from public.json

- Updated topology moveNodeToGroup to use useWarningModal with proper cancel handling
- Updated StopNodeMaintenanceModal to remove imperative API, keeping only hook-based approach
- Updated all metal3-plugin maintenance components to use hook-based approach
- Fixed useWarningModal to properly call closeOverlay() and filter it from props spread

- Created MoveNodeHandlersProvider using React Context pattern
- Created SyncMoveNodeHandlers component to sync handlers for non-React contexts
- Added useMoveNodeHandlers() hook for accessing handlers
- Wrapped Topology component with MoveNodeHandlersProvider
- Deprecated old useSetupMoveNodeToGroupHandlers hooks with warnings
- Added comprehensive unit tests (8 tests covering all scenarios)

- Blur active element before launching confirmation modal
- Prevents focus from remaining on SVG elements in topology graph
- Fixes browser warning: "Blocked aria-hidden on an element because its descendant retained focus"

- Eliminates global state race conditions
- Follows React Context pattern established in openshift#15946
- Improves testability with mockable context
- Single provider initialization around Topology component
- Proper cleanup via React lifecycle
- Type-safe with clear error messages
- Maintains backward compatibility via deprecated hooks
- Resolves accessibility violations with aria-hidden elements

The hybrid approach (Context + module reference) supports both React components
(via useMoveNodeHandlers hook) and non-React drag-drop callbacks (via moveNodeToGroup
function), consistent with the error modal handler pattern.

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

openshift-ci bot commented Feb 5, 2026

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

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

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.

@rhamilto
Copy link
Member Author

rhamilto commented Feb 5, 2026

/retest

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/dev-console Related to dev-console component/knative Related to knative-plugin component/sdk Related to console-plugin-sdk component/shared Related to console-shared component/topology Related to topology 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. kind/i18n Indicates issue or PR relates to internationalization or has content that needs to be translated lgtm Indicates that a PR is ready to be merged. 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.

6 participants