Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6a29773
Migrate TaintsModal from createModalLauncher to useOverlay
sg00dwin Jan 21, 2026
e980afa
Migrate OLM UpdateStrategyModal from createModalLauncher to useOverlay
sg00dwin Jan 22, 2026
fcaef00
Migrate resource-requirements modal
sg00dwin Jan 23, 2026
2a42822
Migrate EditDefaultSourcesModal from createModalLauncher to useOverlay
sg00dwin Jan 23, 2026
41dd7fa
Migrate DisableDefaultSourceModal from createModalLauncher to useOverlay
sg00dwin Jan 23, 2026
5275691
Migrate InstallPlanApprovalModal from createModalLauncher to useOverlay
sg00dwin Jan 27, 2026
b4092b2
Migrate SubscriptionChannelModal from createModalLauncher to useOverlay
sg00dwin Jan 27, 2026
44ee117
Migrate InstallPlanPreviewModal from createModalLauncher to useOverlay
sg00dwin Jan 28, 2026
bf7971e
Migrate UninstallOperatorModal from createModalLauncher to useOverlay
sg00dwin Jan 29, 2026
82d53fb
Use named exports for migrated modal providers
sg00dwin Jan 29, 2026
2b4ccbd
Update launchOverlay to launchMOdal. Remove merge error.
sg00dwin Feb 3, 2026
0bc5e8a
Batch rename of ...ModalProvider to ...ModalOverlay
sg00dwin Feb 3, 2026
290e132
Update comment with what's excluded and why. Rename Launcher to launc…
sg00dwin Feb 3, 2026
0a3ca19
Name changes for consistency
sg00dwin Feb 3, 2026
07bd2dc
minor cleanup
sg00dwin Feb 4, 2026
07fce75
Address PR review feedback on OLM modal migration
sg00dwin Feb 4, 2026
5a80405
Update OLM modals to useOverlay pattern with React.lazy() for code
sg00dwin Feb 9, 2026
8b1a822
Revert back to NodeKind resource and minor cleanup
sg00dwin Feb 10, 2026
aa1b792
Migrate UninstallOperatorModal to React Router v6 and hook pattern
sg00dwin Feb 12, 2026
c77bd25
Remove orphaned delete-catalog-source-modal file
sg00dwin Feb 12, 2026
2863568
Migrate community operator warning modal to useOverlay with lazy loading
sg00dwin Feb 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
LazyAnnotationsModalOverlay,
LazyDeleteModalOverlay,
LazyLabelsModalOverlay,
taintsModal,
tolerationsModal,
} from '@console/internal/components/modals';
import { useConfigureCountModal } from '@console/internal/components/modals/configure-count-modal';
import { TaintsModalOverlay } from '@console/internal/components/modals/taints-modal';
import { asAccessReview } from '@console/internal/components/utils/rbac';
import { resourceObjPath } from '@console/internal/components/utils/resource-link';
import { referenceFor, K8sModel, K8sResourceKind } from '@console/internal/module/k8s';
import { referenceFor, K8sModel, K8sResourceKind, NodeKind } from '@console/internal/module/k8s';
import { CommonActionCreator, ActionObject } from './types';

/**
Expand Down Expand Up @@ -141,9 +141,9 @@ export const useCommonActions = <T extends readonly CommonActionCreator[]>(
id: 'edit-taints',
label: t('console-app~Edit taints'),
cta: () =>
taintsModal({
launchModal(TaintsModalOverlay, {
resourceKind: kind,
resource,
resource: resource as NodeKind,
}),
accessReview: asAccessReview(kind as K8sModel, resource as K8sResourceKind, 'patch'),
}),
Expand All @@ -159,9 +159,12 @@ export const useCommonActions = <T extends readonly CommonActionCreator[]>(
accessReview: asAccessReview(kind as K8sModel, resource as K8sResourceKind, 'patch'),
}),
}),
// Excluding stable modal launcher functions (tolerationsModal, taintsModal)
// to prevent unnecessary re-renders
// TODO: remove once all Modals have been updated to useOverlay
// Excluding legacy modal launcher functions (labelsModalLauncher, annotationsModalLauncher,
// podSelectorModal, tolerationsModal) from dependencies because:
// 1. These are created by createModalLauncher() and are referentially stable
// 2. They don't change between renders, so excluding them prevents unnecessary re-renders
// 3. Once these modals are migrated to useOverlay, they'll use launchModal instead
// TODO: Remove this eslint-disable once all modals have been migrated to useOverlay pattern
// eslint-disable-next-line react-hooks/exhaustive-deps
[kind, resource, t, message, actualEditPath, launchModal],
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useCommonActions } from '@console/app/src/actions/hooks/useCommonAction
import { Action } from '@console/dynamic-plugin-sdk';
import { useDeepCompareMemoize } from '@console/dynamic-plugin-sdk/src/utils/k8s/hooks/useDeepCompareMemoize';
import { asAccessReview } from '@console/internal/components/utils';
import { referenceFor, k8sKill, k8sGet, k8sPatch } from '@console/internal/module/k8s';
import { referenceFor } from '@console/internal/module/k8s';
import { useK8sModel } from '@console/shared/src/hooks/useK8sModel';
import { useUninstallOperatorModal } from '../../components/modals/uninstall-operator-modal';
import { ClusterServiceVersionModel } from '../../models';
Expand All @@ -31,16 +31,12 @@ export const useSubscriptionActions = (
const { t } = useTranslation();
const [model] = useK8sModel(referenceFor(obj));
const [commonActions] = useCommonActions(model, obj, [CommonActionCreator.Edit]);
const uninstallOperatorModal = useUninstallOperatorModal({
k8sKill,
k8sGet,
k8sPatch,
subscription: obj,
});

const memoizedFilterActions = useDeepCompareMemoize(filterActions);
const installedCSV = obj.status?.installedCSV;

const uninstallOperatorModal = useUninstallOperatorModal(obj);

const factory = useMemo(
() => ({
[SubscriptionActionCreator.RemoveSubscription]: () => ({
Expand All @@ -59,8 +55,7 @@ export const useSubscriptionActions = (
};
},
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[installedCSV, model, obj, t],
[installedCSV, model, obj, t, uninstallOperatorModal],
);

// filter and initialize requested actions or construct list of all SubscriptionActions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { CommonActionCreator } from '@console/app/src/actions/hooks/types';
import { useCommonActions } from '@console/app/src/actions/hooks/useCommonActions';
import { useCommonResourceActions } from '@console/app/src/actions/hooks/useCommonResourceActions';
import { Action } from '@console/dynamic-plugin-sdk/src';
import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
import { asAccessReview } from '@console/internal/components/utils/rbac';
import { referenceFor } from '@console/internal/module/k8s';
import { useK8sModel } from '@console/shared/src/hooks/useK8sModel';
import { disableDefaultSourceModal } from '../../components/modals/disable-default-source-modal';
import { LazyDisableDefaultSourceModalOverlay } from '../../components/modals';
import { OperatorHubKind } from '../../components/operator-hub';
import { DEFAULT_SOURCE_NAMESPACE } from '../../const';
import { OperatorHubModel } from '../../models';
Expand All @@ -16,16 +17,22 @@ import useOperatorHubConfig from '../../utils/useOperatorHubConfig';

const useDisableSourceAction = (operatorHub: OperatorHubKind, sourceName: string): Action[] => {
const { t } = useTranslation();
const launchModal = useOverlay();
const factory = useMemo(
() => ({
disableSource: () => ({
id: 'disable-source',
label: t('olm~Disable'),
cta: () => disableDefaultSourceModal({ kind: OperatorHubModel, operatorHub, sourceName }),
cta: () =>
launchModal(LazyDisableDefaultSourceModalOverlay, {
kind: OperatorHubModel,
operatorHub,
sourceName,
}),
accessReview: asAccessReview(OperatorHubModel, operatorHub, 'patch'),
}),
}),
[t, operatorHub, sourceName],
[t, operatorHub, sourceName, launchModal],
);
const action = useMemo(() => [factory.disableSource()], [factory]);
return action;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@ import { useTranslation } from 'react-i18next';
import { K8S_VERB_DELETE } from '@console/dynamic-plugin-sdk/src/api/constants';
import { Action } from '@console/dynamic-plugin-sdk/src/extensions/actions';
import { useOverlay } from '@console/dynamic-plugin-sdk/src/lib-core';
import { k8sGet, k8sKill, k8sPatch } from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-resource';
import { DeleteModalOverlay } from '@console/internal/components/modals/delete-modal';
import { asAccessReview } from '@console/internal/components/utils/rbac';
import { resourceObjPath } from '@console/internal/components/utils/resource-link';
import { referenceFor } from '@console/internal/module/k8s';
import { UninstallOperatorOverlay } from '../components/modals/uninstall-operator-modal';
import { useUninstallOperatorModal } from '../components/modals/uninstall-operator-modal';
import { ClusterServiceVersionModel, SubscriptionModel } from '../models';

const useOperatorActions = ({ resource, subscription }): [Action[], boolean, any] => {
const { t } = useTranslation();
const launchModal = useOverlay();

const uninstallOperatorModal = useUninstallOperatorModal(subscription, resource);

const actions = useMemo(() => {
if (!resource) {
return [];
Expand Down Expand Up @@ -47,19 +48,11 @@ const useOperatorActions = ({ resource, subscription }): [Action[], boolean, any
{
id: 'uninstall-operator',
label: t('olm~Uninstall Operator'),
cta: () =>
launchModal(UninstallOperatorOverlay, {
k8sKill,
k8sGet,
k8sPatch,
subscription,
csv: resource,
blocking: true,
}),
cta: () => uninstallOperatorModal(),
accessReview: asAccessReview(SubscriptionModel, subscription, K8S_VERB_DELETE),
},
];
}, [resource, subscription, t, launchModal]);
}, [resource, subscription, t, launchModal, uninstallOperatorModal]);
return [actions, true, null];
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { screen, waitFor, fireEvent } from '@testing-library/react';
import { Map as ImmutableMap } from 'immutable';
import * as k8sResourceModule from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-resource';
import * as modal from '@console/internal/components/factory/modal';
import { K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
import { renderWithProviders } from '@console/shared/src/test-utils/unit-test-utils';
import { testResourceInstance, testModel } from '../../../../../mocks';
Expand All @@ -11,18 +9,23 @@ import {
ResourceRequirementsModalLink,
} from '../resource-requirements';

const useK8sModelMock = jest.fn();
const useOverlayMock = jest.fn();

jest.mock('@console/dynamic-plugin-sdk/src/utils/k8s/k8s-resource', () => ({
...jest.requireActual('@console/dynamic-plugin-sdk/src/utils/k8s/k8s-resource'),
k8sUpdate: jest.fn(),
}));

jest.mock('@console/internal/components/factory/modal', () => ({
...jest.requireActual('@console/internal/components/factory/modal'),
createModalLauncher: jest.fn(),
jest.mock('@console/shared/src/hooks/useK8sModel', () => ({
useK8sModel: (...args) => useK8sModelMock(...args),
}));

jest.mock('@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay', () => ({
useOverlay: (...args) => useOverlayMock(...args),
}));

const k8sUpdateMock = k8sResourceModule.k8sUpdate as jest.Mock;
const createModalLauncherMock = modal.createModalLauncher as jest.Mock;

describe('ResourceRequirementsModal', () => {
const title = 'TestResource Resource Requests';
Expand Down Expand Up @@ -94,6 +97,7 @@ describe('ResourceRequirementsModal', () => {

describe('ResourceRequirementsModalLink', () => {
let obj: K8sResourceKind;
let launchModalMock: jest.Mock;

beforeEach(() => {
obj = {
Expand All @@ -106,23 +110,17 @@ describe('ResourceRequirementsModalLink', () => {
},
},
};

launchModalMock = jest.fn();
useK8sModelMock.mockReturnValue([testModel, false]);
useOverlayMock.mockReturnValue(launchModalMock);

jest.clearAllMocks();
});

it('should render button link with resource requests', () => {
renderWithProviders(
<ResourceRequirementsModalLink obj={obj} type="requests" path="resources" />,
{
initialState: {
k8s: ImmutableMap({
RESOURCES: ImmutableMap({
models: ImmutableMap({
'testapp.coreos.com~v1alpha1~TestResource': testModel,
}),
}),
}),
},
},
);

const { memory, cpu, 'ephemeral-storage': storage } = obj.spec.resources.requests;
Expand All @@ -132,20 +130,7 @@ describe('ResourceRequirementsModalLink', () => {
});

it('should render button link with resource limits', () => {
renderWithProviders(
<ResourceRequirementsModalLink obj={obj} type="limits" path="resources" />,
{
initialState: {
k8s: ImmutableMap({
RESOURCES: ImmutableMap({
models: ImmutableMap({
'testapp.coreos.com~v1alpha1~TestResource': testModel,
}),
}),
}),
},
},
);
renderWithProviders(<ResourceRequirementsModalLink obj={obj} type="limits" path="resources" />);

const { memory, cpu, 'ephemeral-storage': storage } = obj.spec.resources.limits;
expect(
Expand All @@ -165,17 +150,6 @@ describe('ResourceRequirementsModalLink', () => {
};
renderWithProviders(
<ResourceRequirementsModalLink obj={objWithoutResources} type="limits" path="resources" />,
{
initialState: {
k8s: ImmutableMap({
RESOURCES: ImmutableMap({
models: ImmutableMap({
'testapp.coreos.com~v1alpha1~TestResource': testModel,
}),
}),
}),
},
},
);

expect(
Expand All @@ -186,38 +160,22 @@ describe('ResourceRequirementsModalLink', () => {
});

it('should open resource requirements modal when button is clicked', async () => {
const modalSpy = jest.fn();
createModalLauncherMock.mockReturnValue(modalSpy);

renderWithProviders(
<ResourceRequirementsModalLink obj={obj} type="limits" path="resources" />,
{
initialState: {
k8s: ImmutableMap({
RESOURCES: ImmutableMap({
models: ImmutableMap({
'testapp.coreos.com~v1alpha1~TestResource': testModel,
}),
}),
}),
},
},
);
renderWithProviders(<ResourceRequirementsModalLink obj={obj} type="limits" path="resources" />);

fireEvent.click(screen.getByRole('button'));

await waitFor(() => {
expect(modalSpy).toHaveBeenCalled();
expect(launchModalMock).toHaveBeenCalled();
});

const modalArgs = modalSpy.mock.calls[0][0];
expect(modalArgs.title).toEqual(`${obj.kind} Resource Limits`);
expect(modalArgs.description).toEqual(
const [, modalProps] = launchModalMock.mock.calls[0];
expect(modalProps.title).toEqual(`${obj.kind} Resource Limits`);
expect(modalProps.description).toEqual(
'Define the resource limits for this TestResource instance.',
);
expect(modalArgs.obj).toEqual(obj);
expect(modalArgs.model).toEqual(testModel);
expect(modalArgs.type).toEqual('limits');
expect(modalArgs.path).toEqual('resources');
expect(modalProps.obj).toEqual(obj);
expect(modalProps.model).toEqual(testModel);
expect(modalProps.type).toEqual('limits');
expect(modalProps.path).toEqual('resources');
});
});
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import { useCallback } from 'react';
import { useOverlay } from '@console/dynamic-plugin-sdk/src/app/modal-support/useOverlay';
import i18n from '@console/internal/i18n';
import { K8sKind, K8sResourceKind } from '@console/internal/module/k8s';
import { updateStrategyModal } from '../../modals/update-strategy-modal';
import { LazyUpdateStrategyModalOverlay } from '../../modals';
import { Descriptor } from '../types';
import { getPatchPathFromDescriptor } from '../utils';

export const configureUpdateStrategyModal = ({
export const useConfigureUpdateStrategyModal = ({
kindObj,
resource,
specDescriptor,
specValue,
}: ConfigureUpdateStrategyModalProps) => {
return updateStrategyModal({
resourceKind: kindObj,
resource,
defaultValue: specValue,
title: i18n.t('olm~Edit {{item}}', { item: specDescriptor.displayName }),
path: `/spec/${getPatchPathFromDescriptor(specDescriptor)}`,
});
const launchModal = useOverlay();

return useCallback(() => {
return launchModal(LazyUpdateStrategyModalOverlay, {
resourceKind: kindObj,
resource,
defaultValue: specValue,
title: i18n.t('olm~Edit {{item}}', { item: specDescriptor.displayName }),
path: `/spec/${getPatchPathFromDescriptor(specDescriptor)}`,
});
}, [launchModal, kindObj, resource, specValue, specDescriptor]);
};

type ConfigureUpdateStrategyModalProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { DefaultCapability, K8sResourceLinkCapability, SecretCapability } from '
import { CapabilityProps, SpecCapability, Error } from '../types';
import { getPatchPathFromDescriptor, getValidCapabilitiesForValue } from '../utils';
import { useConfigureSizeModal } from './configure-size';
import { configureUpdateStrategyModal } from './configure-update-strategy';
import { useConfigureUpdateStrategyModal } from './configure-update-strategy';
import { EndpointList, EndpointListProps } from './endpoint';
import { ResourceRequirementsModalLink } from './resource-requirements';

Expand Down Expand Up @@ -287,19 +287,19 @@ const UpdateStrategy: FC<SpecCapabilityProps> = ({
value,
}) => {
const { t } = useTranslation();
const launchUpdateStrategyModal = useConfigureUpdateStrategyModal({
kindObj: model,
resource: obj,
specDescriptor: descriptor,
specValue: value,
});

return (
<DetailsItem
description={description}
label={label}
obj={obj}
onEdit={() =>
configureUpdateStrategyModal({
kindObj: model,
resource: obj,
specDescriptor: descriptor,
specValue: value,
})
}
onEdit={launchUpdateStrategyModal}
path={fullPath}
>
{value?.type ?? t('public~None')}
Expand Down
Loading