From 8c6481ae8f1420e96d5c5325596cdfaa4864e499 Mon Sep 17 00:00:00 2001
From: Steve Goodwin
Date: Tue, 20 Jan 2026 13:45:01 -0500
Subject: [PATCH 1/2] Migrate DeleteHPAModal and ConsolePluginModal from
createModalLauncher to useOverlay Assisted by: Claude Code
---
.../src/actions/creators/hpa-factory.ts | 124 +++++++++---------
.../ConsoleOperatorConfig.tsx | 6 +-
.../src/components/hpa/DeleteHPAModal.tsx | 27 +++-
.../src/components/hpa/index.ts | 2 +-
.../components/modals/ConsolePluginModal.tsx | 52 +++++---
.../src/components/modals/index.ts | 2 +-
.../src/components/clusterserviceversion.tsx | 6 +-
7 files changed, 128 insertions(+), 91 deletions(-)
diff --git a/frontend/packages/console-app/src/actions/creators/hpa-factory.ts b/frontend/packages/console-app/src/actions/creators/hpa-factory.ts
index 5ce95f4d1c3..53676d3db38 100644
--- a/frontend/packages/console-app/src/actions/creators/hpa-factory.ts
+++ b/frontend/packages/console-app/src/actions/creators/hpa-factory.ts
@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import i18next from 'i18next';
import { Action } from '@console/dynamic-plugin-sdk';
+import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
import { useK8sWatchResources } from '@console/internal/components/utils/k8s-watch-hook';
import { HorizontalPodAutoscalerModel } from '@console/internal/models';
import {
@@ -14,84 +15,23 @@ import {
ClusterServiceVersionModel,
ClusterServiceVersionKind,
} from '@console/operator-lifecycle-manager';
-import deleteHPAModal from '@console/shared/src/components/hpa/DeleteHPAModal';
+import DeleteHPAModalProvider from '@console/shared/src/components/hpa/DeleteHPAModal';
import { isHelmResource } from '@console/shared/src/utils/helm-utils';
import { doesHpaMatch } from '@console/shared/src/utils/hpa-utils';
import { isOperatorBackedService } from '@console/shared/src/utils/operator-utils';
-import { ResourceActionFactory } from './types';
const hpaRoute = (
{ metadata: { name = '', namespace = '' } = {} }: K8sResourceCommon,
kind: K8sKind,
) => `/workload-hpa/ns/${namespace}/${referenceForModel(kind)}/${name}`;
-export const HpaActionFactory: ResourceActionFactory = {
- AddHorizontalPodAutoScaler: (kind: K8sKind, obj: K8sResourceKind) => ({
- id: 'add-hpa',
- label: i18next.t('console-app~Add HorizontalPodAutoscaler'),
- cta: { href: hpaRoute(obj, kind) },
- insertBefore: 'add-pdb',
- accessReview: {
- group: HorizontalPodAutoscalerModel.apiGroup,
- resource: HorizontalPodAutoscalerModel.plural,
- namespace: obj.metadata?.namespace,
- verb: 'create',
- },
- }),
- EditHorizontalPodAutoScaler: (kind: K8sKind, obj: K8sResourceCommon) => ({
- id: 'edit-hpa',
- label: i18next.t('console-app~Edit HorizontalPodAutoscaler'),
- cta: { href: hpaRoute(obj, kind) },
- insertBefore: 'add-pdb',
- accessReview: {
- group: HorizontalPodAutoscalerModel.apiGroup,
- resource: HorizontalPodAutoscalerModel.plural,
- namespace: obj.metadata?.namespace,
- verb: 'update',
- },
- }),
- DeleteHorizontalPodAutoScaler: (
- kind: K8sKind,
- obj: K8sResourceCommon,
- relatedHPA: HorizontalPodAutoscalerKind,
- ) => ({
- id: 'delete-hpa',
- label: i18next.t('console-app~Remove HorizontalPodAutoscaler'),
- insertBefore: 'delete-pdb',
- cta: () => {
- deleteHPAModal({
- workload: obj,
- hpa: relatedHPA,
- });
- },
- accessReview: {
- group: HorizontalPodAutoscalerModel.apiGroup,
- resource: HorizontalPodAutoscalerModel.plural,
- namespace: obj.metadata?.namespace,
- verb: 'delete',
- },
- }),
-};
-
-export const getHpaActions = (
- kind: K8sKind,
- obj: K8sResourceKind,
- relatedHPAs: K8sResourceKind[],
-): Action[] => {
- if (relatedHPAs.length === 0) return [HpaActionFactory.AddHorizontalPodAutoScaler(kind, obj)];
-
- return [
- HpaActionFactory.EditHorizontalPodAutoScaler(kind, obj),
- HpaActionFactory.DeleteHorizontalPodAutoScaler(kind, obj, relatedHPAs[0]),
- ];
-};
-
type DeploymentActionExtraResources = {
hpas: HorizontalPodAutoscalerKind[];
csvs: ClusterServiceVersionKind[];
};
export const useHPAActions = (kindObj: K8sKind, resource: K8sResourceKind) => {
+ const launchOverlay = useOverlay();
const namespace = resource?.metadata?.namespace;
const watchedResources = useMemo(
@@ -123,9 +63,63 @@ export const useHPAActions = (kindObj: K8sKind, resource: K8sResourceKind) => {
[extraResources.csvs.data, resource],
);
- const result = useMemo<[Action[], HorizontalPodAutoscalerKind[]]>(() => {
- return [supportsHPA ? getHpaActions(kindObj, resource, relatedHPAs) : [], relatedHPAs];
+ const actions = useMemo(() => {
+ if (!supportsHPA) return [];
+
+ if (relatedHPAs.length === 0) {
+ return [
+ {
+ id: 'add-hpa',
+ label: i18next.t('console-app~Add HorizontalPodAutoscaler'),
+ cta: { href: hpaRoute(resource, kindObj) },
+ insertBefore: 'add-pdb',
+ accessReview: {
+ group: HorizontalPodAutoscalerModel.apiGroup,
+ resource: HorizontalPodAutoscalerModel.plural,
+ namespace: resource.metadata?.namespace,
+ verb: 'create',
+ },
+ },
+ ];
+ }
+ return [
+ {
+ id: 'edit-hpa',
+ label: i18next.t('console-app~Edit HorizontalPodAutoscaler'),
+ cta: { href: hpaRoute(resource, kindObj) },
+ insertBefore: 'add-pdb',
+ accessReview: {
+ group: HorizontalPodAutoscalerModel.apiGroup,
+ resource: HorizontalPodAutoscalerModel.plural,
+ namespace: resource.metadata?.namespace,
+ verb: 'update',
+ },
+ },
+ {
+ id: 'delete-hpa',
+ label: i18next.t('console-app~Remove HorizontalPodAutoscaler'),
+ insertBefore: 'delete-pdb',
+ cta: () => {
+ launchOverlay(DeleteHPAModalProvider, {
+ workload: resource,
+ hpa: relatedHPAs[0],
+ });
+ },
+ accessReview: {
+ group: HorizontalPodAutoscalerModel.apiGroup,
+ resource: HorizontalPodAutoscalerModel.plural,
+ namespace: resource.metadata?.namespace,
+ verb: 'delete',
+ },
+ },
+ ];
+ // missing launchModal dependency, that causes max depth exceeded error
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [kindObj, relatedHPAs, resource, supportsHPA]);
+ const result = useMemo<[Action[], HorizontalPodAutoscalerKind[]]>(() => {
+ return [actions, relatedHPAs];
+ }, [actions, relatedHPAs]);
+
return result;
};
diff --git a/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx b/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx
index da010ad4111..a1a0a1c89fc 100644
--- a/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx
+++ b/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx
@@ -19,6 +19,7 @@ import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { useLocation } from 'react-router-dom-v5-compat';
import { useAccessReview, WatchK8sResource } from '@console/dynamic-plugin-sdk';
+import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
import {
getGroupVersionKindForModel,
getReferenceForModel,
@@ -43,7 +44,7 @@ import {
import { RootState } from '@console/internal/redux';
import { usePluginInfo } from '@console/plugin-sdk/src/api/usePluginInfo';
import PaneBody from '@console/shared/src/components/layout/PaneBody';
-import { consolePluginModal } from '@console/shared/src/components/modals/ConsolePluginModal';
+import ConsolePluginModalProvider from '@console/shared/src/components/modals/ConsolePluginModal';
import {
GreenCheckCircleIcon,
YellowExclamationTriangleIcon,
@@ -97,6 +98,7 @@ export const ConsolePluginEnabledStatus: FC = (
enabled,
}) => {
const { t } = useTranslation();
+ const launchOverlay = useOverlay();
const {
consoleOperatorConfig,
@@ -116,7 +118,7 @@ export const ConsolePluginEnabledStatus: FC = (
type="button"
isInline
onClick={() =>
- consolePluginModal({
+ launchOverlay(ConsolePluginModalProvider, {
consoleOperatorConfig,
pluginName,
trusted: false,
diff --git a/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx b/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx
index a343b0a5d6f..4ee04aa7640 100644
--- a/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx
+++ b/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx
@@ -4,12 +4,13 @@ import { Form } from '@patternfly/react-core';
import { ExclamationTriangleIcon } from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
import { t_global_icon_color_status_warning_default as warningColor } from '@patternfly/react-tokens';
import { useTranslation } from 'react-i18next';
+import { OverlayComponent } from '@console/dynamic-plugin-sdk/src/app/modal-support/OverlayProvider';
import {
- createModalLauncher,
ModalBody,
ModalComponentProps,
ModalSubmitFooter,
ModalTitle,
+ ModalWrapper,
} from '@console/internal/components/factory/modal';
import { LoadingInline } from '@console/internal/components/utils/status-box';
import { HorizontalPodAutoscalerModel } from '@console/internal/models';
@@ -24,7 +25,7 @@ type DeleteHPAModalProps = ModalComponentProps & {
workload: K8sResourceCommon;
};
-const DeleteHPAModal: FC = ({ close, hpa, workload }) => {
+const DeleteHPAModal: FC = ({ close, cancel, hpa, workload }) => {
const [submitError, setSubmitError] = useState(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const { t } = useTranslation();
@@ -82,11 +83,29 @@ const DeleteHPAModal: FC = ({ close, hpa, workload }) => {
submitText={t('console-shared~Remove')}
submitDanger
submitDisabled={!!submitError}
- cancel={close}
+ cancel={cancel}
/>
);
};
-export default createModalLauncher(DeleteHPAModal);
+type DeleteHPAModalProviderProps = {
+ hpa: HorizontalPodAutoscalerKind;
+ workload: K8sResourceCommon;
+};
+
+const DeleteHPAModalProvider: OverlayComponent = (props) => {
+ return (
+
+
+
+ );
+};
+
+export default DeleteHPAModalProvider;
diff --git a/frontend/packages/console-shared/src/components/hpa/index.ts b/frontend/packages/console-shared/src/components/hpa/index.ts
index fc131d9b545..de11a4852e1 100644
--- a/frontend/packages/console-shared/src/components/hpa/index.ts
+++ b/frontend/packages/console-shared/src/components/hpa/index.ts
@@ -1 +1 @@
-export { default as deleteHPAModal } from './DeleteHPAModal';
+export { default as DeleteHPAModalProvider } from './DeleteHPAModal';
diff --git a/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx b/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx
index 8a4d1306c69..557b11273be 100644
--- a/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx
+++ b/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx
@@ -1,11 +1,11 @@
import { useState } from 'react';
-import { Form } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
+import { OverlayComponent } from '@console/dynamic-plugin-sdk/src/app/modal-support/OverlayProvider';
import {
- createModalLauncher,
ModalTitle,
ModalBody,
ModalSubmitFooter,
+ ModalWrapper,
} from '@console/internal/components/factory/modal';
import { ConsoleOperatorConfigModel } from '@console/internal/models';
import { k8sPatch, K8sResourceKind } from '@console/internal/module/k8s';
@@ -48,19 +48,17 @@ export const ConsolePluginModal = (props: ConsolePluginModalProps) => {
'console-shared~This console plugin provides a custom interface that can be included in the console. Updating the enablement of this console plugin will prompt for the console to be refreshed once it has been updated. Make sure you trust this console plugin before enabling.',
)}
-
+
+
{
);
};
-export const consolePluginModal = createModalLauncher(ConsolePluginModal);
+type ConsolePluginModalProviderProps = {
+ consoleOperatorConfig: K8sResourceKind;
+ csvPluginsCount?: number;
+ pluginName: string;
+ trusted: boolean;
+};
+
+const ConsolePluginModalProvider: OverlayComponent = (props) => {
+ return (
+
+
+
+ );
+};
+
+export default ConsolePluginModalProvider;
export type ConsolePluginModalProps = {
consoleOperatorConfig: K8sResourceKind;
diff --git a/frontend/packages/console-shared/src/components/modals/index.ts b/frontend/packages/console-shared/src/components/modals/index.ts
index ac902b242de..53f05c1275a 100644
--- a/frontend/packages/console-shared/src/components/modals/index.ts
+++ b/frontend/packages/console-shared/src/components/modals/index.ts
@@ -1,6 +1,6 @@
export const consolePluginModal = (props) =>
import('./ConsolePluginModal' /* webpackChunkName: "shared-modals" */).then((m) =>
- m.consolePluginModal(props),
+ m.default(props),
);
export const deleteResourceModal = (props) =>
diff --git a/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx b/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
index 4657eca88f4..e70813514f8 100644
--- a/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
+++ b/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
@@ -30,6 +30,7 @@ import {
useAccessReviewAllowed,
useAccessReview,
} from '@console/dynamic-plugin-sdk';
+import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
import { getGroupVersionKindForModel } from '@console/dynamic-plugin-sdk/src/lib-core';
import { Conditions, ConditionTypes } from '@console/internal/components/conditions';
import { ResourceEventStream } from '@console/internal/components/events';
@@ -77,7 +78,7 @@ import { DocumentTitle } from '@console/shared/src/components/document-title/Doc
import { withFallback } from '@console/shared/src/components/error';
import PaneBody from '@console/shared/src/components/layout/PaneBody';
import { ExternalLink } from '@console/shared/src/components/links/ExternalLink';
-import { consolePluginModal } from '@console/shared/src/components/modals';
+import ConsolePluginModalProvider from '@console/shared/src/components/modals/ConsolePluginModal';
import { RedExclamationCircleIcon } from '@console/shared/src/components/status/icons';
import { CONSOLE_OPERATOR_CONFIG_NAME } from '@console/shared/src/constants';
import { useActiveNamespace } from '@console/shared/src/hooks/redux-selectors';
@@ -239,6 +240,7 @@ const ManagedNamespaces: FC = ({ obj }) => {
};
const ConsolePlugins: FC = ({ csvPlugins, trusted }) => {
+ const launchOverlay = useOverlay();
const console: WatchK8sResource = {
kind: referenceForModel(ConsoleOperatorConfigModel),
isList: false,
@@ -273,7 +275,7 @@ const ConsolePlugins: FC = ({ csvPlugins, trusted }) => {
type="button"
isInline
onClick={() =>
- consolePluginModal({
+ launchOverlay(ConsolePluginModalProvider, {
consoleOperatorConfig,
pluginName,
trusted,
From 0eb2387c84c35f2723b96fec48a120b17f18707d Mon Sep 17 00:00:00 2001
From: Steve Goodwin
Date: Fri, 30 Jan 2026 17:21:25 -0500
Subject: [PATCH 2/2] Address comments
Assisted by: Claude Code
---
.../console-app/src/actions/creators/hpa-factory.ts | 2 +-
.../components/console-operator/ConsoleOperatorConfig.tsx | 4 ++--
.../console-shared/src/components/hpa/DeleteHPAModal.tsx | 2 +-
.../packages/console-shared/src/components/hpa/index.ts | 2 +-
.../src/components/modals/ConsolePluginModal.tsx | 6 +++---
.../packages/console-shared/src/components/modals/index.ts | 2 +-
.../src/components/clusterserviceversion.tsx | 4 ++--
7 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/frontend/packages/console-app/src/actions/creators/hpa-factory.ts b/frontend/packages/console-app/src/actions/creators/hpa-factory.ts
index 53676d3db38..f672399a2bf 100644
--- a/frontend/packages/console-app/src/actions/creators/hpa-factory.ts
+++ b/frontend/packages/console-app/src/actions/creators/hpa-factory.ts
@@ -15,7 +15,7 @@ import {
ClusterServiceVersionModel,
ClusterServiceVersionKind,
} from '@console/operator-lifecycle-manager';
-import DeleteHPAModalProvider from '@console/shared/src/components/hpa/DeleteHPAModal';
+import { DeleteHPAModalProvider } from '@console/shared/src/components/hpa/DeleteHPAModal';
import { isHelmResource } from '@console/shared/src/utils/helm-utils';
import { doesHpaMatch } from '@console/shared/src/utils/hpa-utils';
import { isOperatorBackedService } from '@console/shared/src/utils/operator-utils';
diff --git a/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx b/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx
index a1a0a1c89fc..92407e4f5dc 100644
--- a/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx
+++ b/frontend/packages/console-app/src/components/console-operator/ConsoleOperatorConfig.tsx
@@ -44,7 +44,7 @@ import {
import { RootState } from '@console/internal/redux';
import { usePluginInfo } from '@console/plugin-sdk/src/api/usePluginInfo';
import PaneBody from '@console/shared/src/components/layout/PaneBody';
-import ConsolePluginModalProvider from '@console/shared/src/components/modals/ConsolePluginModal';
+import { ConsolePluginModalOverlay } from '@console/shared/src/components/modals/ConsolePluginModal';
import {
GreenCheckCircleIcon,
YellowExclamationTriangleIcon,
@@ -118,7 +118,7 @@ export const ConsolePluginEnabledStatus: FC = (
type="button"
isInline
onClick={() =>
- launchOverlay(ConsolePluginModalProvider, {
+ launchOverlay(ConsolePluginModalOverlay, {
consoleOperatorConfig,
pluginName,
trusted: false,
diff --git a/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx b/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx
index 4ee04aa7640..0e55164dfb1 100644
--- a/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx
+++ b/frontend/packages/console-shared/src/components/hpa/DeleteHPAModal.tsx
@@ -108,4 +108,4 @@ const DeleteHPAModalProvider: OverlayComponent = (p
);
};
-export default DeleteHPAModalProvider;
+export { DeleteHPAModalProvider };
diff --git a/frontend/packages/console-shared/src/components/hpa/index.ts b/frontend/packages/console-shared/src/components/hpa/index.ts
index de11a4852e1..7156a812f4d 100644
--- a/frontend/packages/console-shared/src/components/hpa/index.ts
+++ b/frontend/packages/console-shared/src/components/hpa/index.ts
@@ -1 +1 @@
-export { default as DeleteHPAModalProvider } from './DeleteHPAModal';
+export { DeleteHPAModalProvider } from './DeleteHPAModal';
diff --git a/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx b/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx
index 557b11273be..205bc0b97cf 100644
--- a/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx
+++ b/frontend/packages/console-shared/src/components/modals/ConsolePluginModal.tsx
@@ -71,14 +71,14 @@ export const ConsolePluginModal = (props: ConsolePluginModalProps) => {
);
};
-type ConsolePluginModalProviderProps = {
+type ConsolePluginModalOverlayProps = {
consoleOperatorConfig: K8sResourceKind;
csvPluginsCount?: number;
pluginName: string;
trusted: boolean;
};
-const ConsolePluginModalProvider: OverlayComponent = (props) => {
+const ConsolePluginModalOverlay: OverlayComponent = (props) => {
return (
import('./ConsolePluginModal' /* webpackChunkName: "shared-modals" */).then((m) =>
- m.default(props),
+ m.ConsolePluginModalOverlay(props),
);
export const deleteResourceModal = (props) =>
diff --git a/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx b/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
index e70813514f8..d3063fa3e8f 100644
--- a/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
+++ b/frontend/packages/operator-lifecycle-manager/src/components/clusterserviceversion.tsx
@@ -78,7 +78,7 @@ import { DocumentTitle } from '@console/shared/src/components/document-title/Doc
import { withFallback } from '@console/shared/src/components/error';
import PaneBody from '@console/shared/src/components/layout/PaneBody';
import { ExternalLink } from '@console/shared/src/components/links/ExternalLink';
-import ConsolePluginModalProvider from '@console/shared/src/components/modals/ConsolePluginModal';
+import { ConsolePluginModalOverlay } from '@console/shared/src/components/modals/ConsolePluginModal';
import { RedExclamationCircleIcon } from '@console/shared/src/components/status/icons';
import { CONSOLE_OPERATOR_CONFIG_NAME } from '@console/shared/src/constants';
import { useActiveNamespace } from '@console/shared/src/hooks/redux-selectors';
@@ -275,7 +275,7 @@ const ConsolePlugins: FC = ({ csvPlugins, trusted }) => {
type="button"
isInline
onClick={() =>
- launchOverlay(ConsolePluginModalProvider, {
+ launchOverlay(ConsolePluginModalOverlay, {
consoleOperatorConfig,
pluginName,
trusted,