diff --git a/frontend/packages/console-app/src/components/favorite/FavoriteButton.tsx b/frontend/packages/console-app/src/components/favorite/FavoriteButton.tsx index dd757764047..22ca166d626 100644 --- a/frontend/packages/console-app/src/components/favorite/FavoriteButton.tsx +++ b/frontend/packages/console-app/src/components/favorite/FavoriteButton.tsx @@ -9,10 +9,13 @@ import { HelperTextItem, TextInput, Tooltip, + Modal, + ModalVariant, + ModalHeader, + ModalBody, + ModalFooter, } from '@patternfly/react-core'; -import { ModalVariant } from '@patternfly/react-core/deprecated'; import { useTranslation } from 'react-i18next'; -import Modal from '@console/shared/src/components/modal/Modal'; import { useTelemetry } from '@console/shared/src/hooks/useTelemetry'; import { useUserSettingsCompatibility } from '@console/shared/src/hooks/useUserSettingsCompatibility'; import { FAVORITES_CONFIG_MAP_KEY, FAVORITES_LOCAL_STORAGE_KEY } from '../../consts'; @@ -78,7 +81,8 @@ export const FavoriteButton = ({ defaultName }: FavoriteButtonProps) => { setIsModalOpen(false); }; - const handleConfirmStar = () => { + const handleConfirmStar = (e?: React.FormEvent) => { + e?.preventDefault(); const trimmedName = name.trim(); if (!trimmedName) { setError(t('Name is required.')); @@ -151,46 +155,39 @@ export const FavoriteButton = ({ defaultName }: FavoriteButtonProps) => { {isModalOpen && ( - + + + +
+ + handleNameChange(v)} + value={name || ''} + autoFocus + required + /> + {error && ( + + + {error} + + + )} + +
+
+ + , + , - ]} - variant={ModalVariant.small} - > -
- - handleNameChange(v)} - value={name || ''} - autoFocus - required - /> - {error && ( - - - {error} - - - )} - -
+ +
)} diff --git a/frontend/packages/console-app/src/components/modals/add-group-users-modal.tsx b/frontend/packages/console-app/src/components/modals/add-group-users-modal.tsx index 01020acec90..467af9072c0 100644 --- a/frontend/packages/console-app/src/components/modals/add-group-users-modal.tsx +++ b/frontend/packages/console-app/src/components/modals/add-group-users-modal.tsx @@ -90,9 +90,6 @@ const AddGroupUsersModal: OverlayComponent = ({ group, )} - +
); diff --git a/frontend/packages/console-app/src/components/tour/TourStepComponent.tsx b/frontend/packages/console-app/src/components/tour/TourStepComponent.tsx index 522560f07b1..be051fa8ad7 100644 --- a/frontend/packages/console-app/src/components/tour/TourStepComponent.tsx +++ b/frontend/packages/console-app/src/components/tour/TourStepComponent.tsx @@ -3,6 +3,7 @@ import { useContext } from 'react'; import { Grid, GridItem, + Modal, ModalBody, ModalFooter, ModalHeader, @@ -10,7 +11,6 @@ import { } from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { ThemeContext } from '@console/internal/components/ThemeProvider'; -import Modal from '@console/shared/src/components/modal/Modal'; import { PopoverPlacement } from '@console/shared/src/components/popover/const'; import Popover from '@console/shared/src/components/popover/Popover'; import Spotlight from '@console/shared/src/components/spotlight/Spotlight'; @@ -108,7 +108,6 @@ const TourStepComponent: FC = ({ id="guided-tour-modal" data-test="guided-tour-modal" aria-label={t('console-app~guided tour {{step, number}}', { step })} - isFullScreen > diff --git a/frontend/packages/console-shared/src/components/modal/Modal.scss b/frontend/packages/console-shared/src/components/catalog/details/CatalogDetailsModal.scss similarity index 100% rename from frontend/packages/console-shared/src/components/modal/Modal.scss rename to frontend/packages/console-shared/src/components/catalog/details/CatalogDetailsModal.scss diff --git a/frontend/packages/console-shared/src/components/catalog/details/CatalogDetailsModal.tsx b/frontend/packages/console-shared/src/components/catalog/details/CatalogDetailsModal.tsx index 3b4c0a60287..2fbc1c2fb8a 100644 --- a/frontend/packages/console-shared/src/components/catalog/details/CatalogDetailsModal.tsx +++ b/frontend/packages/console-shared/src/components/catalog/details/CatalogDetailsModal.tsx @@ -1,14 +1,23 @@ import type { FC } from 'react'; import { CatalogItemHeader } from '@patternfly/react-catalog-view-extension'; -import { Split, SplitItem, Divider, Stack, StackItem } from '@patternfly/react-core'; +import { + Split, + SplitItem, + Divider, + Stack, + StackItem, + Modal, + ModalBody, + ModalHeader, +} from '@patternfly/react-core'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom-v5-compat'; import { CatalogItem } from '@console/dynamic-plugin-sdk/src/extensions'; -import { Modal } from '../../modal'; import CatalogBadges from '../CatalogBadges'; import { useCtaLink } from '../hooks/useCtaLink'; import { getIconProps } from '../utils/catalog-utils'; import CatalogDetailsPanel from './CatalogDetailsPanel'; +import './CatalogDetailsModal.scss'; type CatalogDetailsModalProps = { item: CatalogItem; @@ -42,43 +51,45 @@ const CatalogDetailsModal: FC = ({ item, onClose }) => return ( - - - - - {to && ( -
- - {label} - -
- )} -
- - - {badges?.length > 0 ? : undefined} - -
-
- - - - - - -
+ {modalHeader} + + + + + + {to && ( +
+ + {label} + +
+ )} +
+ + + {badges?.length > 0 ? : undefined} + +
+
+ + + + + + +
+
); }; diff --git a/frontend/packages/console-shared/src/components/index.ts b/frontend/packages/console-shared/src/components/index.ts index 8a4b3e379b6..c1fdd23fe00 100644 --- a/frontend/packages/console-shared/src/components/index.ts +++ b/frontend/packages/console-shared/src/components/index.ts @@ -15,7 +15,6 @@ export * from './virtualized-grid'; export * from './alerts'; export * from './popover'; export * from './utils'; -export * from './modal'; export * from './modals'; export * from './hpa'; export * from './multi-tab-list'; diff --git a/frontend/packages/console-shared/src/components/modal-error-content/ModalErrorContent.tsx b/frontend/packages/console-shared/src/components/modal-error-content/ModalErrorContent.tsx new file mode 100644 index 00000000000..9173d21e88a --- /dev/null +++ b/frontend/packages/console-shared/src/components/modal-error-content/ModalErrorContent.tsx @@ -0,0 +1,40 @@ +import type { FC, ReactNode } from 'react'; +import { HelperText, HelperTextItem } from '@patternfly/react-core'; + +export interface ModalErrorContentProps { + /** The error message to display */ + errorMessage?: ReactNode; + /** Optional additional CSS class names */ + className?: string; + /** Optional data-test attribute for testing */ + 'data-test'?: string; +} + +/** + * Displays an error message in a modal footer using PatternFly HelperText. + * + * @example + * ```tsx + * + * + * {/* modal footer buttons *\/} + * + * ``` + */ +export const ModalErrorContent: FC = ({ + errorMessage, + className = 'pf-v6-u-w-100 pf-v6-u-mb-md', + 'data-test': dataTest = 'modal-error', +}) => { + if (!errorMessage) { + return null; + } + + return ( + + + {errorMessage} + + + ); +}; diff --git a/frontend/packages/console-shared/src/components/modal-error-content/index.ts b/frontend/packages/console-shared/src/components/modal-error-content/index.ts new file mode 100644 index 00000000000..4694ec19344 --- /dev/null +++ b/frontend/packages/console-shared/src/components/modal-error-content/index.ts @@ -0,0 +1 @@ +export * from './ModalErrorContent'; diff --git a/frontend/packages/console-shared/src/components/modal/Modal.tsx b/frontend/packages/console-shared/src/components/modal/Modal.tsx deleted file mode 100644 index e56388aefc6..00000000000 --- a/frontend/packages/console-shared/src/components/modal/Modal.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import type { LegacyRef, FC } from 'react'; -import { Modal as PfModal, ModalProps as PfModalProps } from '@patternfly/react-core/deprecated'; -import { css } from '@patternfly/react-styles'; -import './Modal.scss'; - -type ModalProps = { - isFullScreen?: boolean; - ref?: LegacyRef; -} & PfModalProps; - -const Modal: FC = ({ isFullScreen = false, className, ...props }) => ( - (isFullScreen ? document.body : document.querySelector('#modal-container'))} - /> -); - -export default Modal; diff --git a/frontend/packages/console-shared/src/components/modal/index.ts b/frontend/packages/console-shared/src/components/modal/index.ts deleted file mode 100644 index c6b35681c7e..00000000000 --- a/frontend/packages/console-shared/src/components/modal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as Modal } from './Modal'; diff --git a/frontend/packages/console-shared/src/components/modals/CreateNamespaceModal.tsx b/frontend/packages/console-shared/src/components/modals/CreateNamespaceModal.tsx index 38289ec9b6c..3c189e6dce7 100644 --- a/frontend/packages/console-shared/src/components/modals/CreateNamespaceModal.tsx +++ b/frontend/packages/console-shared/src/components/modals/CreateNamespaceModal.tsx @@ -169,7 +169,7 @@ export const CreateNamespaceModal: ModalComponent = ({ ,     - @@ -64,7 +64,7 @@ const TestFunctionModal: FC = (props) => {   - diff --git a/frontend/packages/metal3-plugin/src/components/modals/RestartHostModal.tsx b/frontend/packages/metal3-plugin/src/components/modals/RestartHostModal.tsx index bee4c03eb40..11c05ea1a09 100644 --- a/frontend/packages/metal3-plugin/src/components/modals/RestartHostModal.tsx +++ b/frontend/packages/metal3-plugin/src/components/modals/RestartHostModal.tsx @@ -55,7 +55,7 @@ const RestartHostModal: OverlayComponent = (props) => { - diff --git a/frontend/packages/metal3-plugin/src/components/modals/StartNodeMaintenanceModal.tsx b/frontend/packages/metal3-plugin/src/components/modals/StartNodeMaintenanceModal.tsx index d6fa6a2815b..3566db9e5d3 100644 --- a/frontend/packages/metal3-plugin/src/components/modals/StartNodeMaintenanceModal.tsx +++ b/frontend/packages/metal3-plugin/src/components/modals/StartNodeMaintenanceModal.tsx @@ -103,7 +103,7 @@ export const StartNodeMaintenanceModal: OverlayComponent {t('metal3-plugin~Start Maintenance')} - diff --git a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-items.tsx b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-items.tsx index 6880bb3babb..14bfec09b10 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-items.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/operator-hub/operator-hub-items.tsx @@ -8,6 +8,9 @@ import { EmptyStateFooter, EmptyStateVariant, Truncate, + Modal, + ModalBody, + ModalHeader, } from '@patternfly/react-core'; import { css } from '@patternfly/react-styles'; import * as _ from 'lodash'; @@ -19,7 +22,6 @@ import { TileViewPage } from '@console/internal/components/utils/tile-view-page' import i18n from '@console/internal/i18n'; import { GreenCheckCircleIcon, - Modal, COMMUNITY_PROVIDERS_WARNING_LOCAL_STORAGE_KEY as storeKey, COMMUNITY_PROVIDERS_WARNING_USERSETTINGS_KEY as userSettingsKey, useUserSettingsCompatibility, @@ -43,6 +45,7 @@ import { validSubscriptionSort, } from './operator-hub-utils'; import { InfrastructureFeature, OperatorHubItem, TokenizedAuthProvider } from './index'; +import '@console/shared/src/components/catalog/details/CatalogDetailsModal.scss'; // Scoring and priority code no longer used and will be removed with Operator Hub catalog files cleanup effort const SCORE = { @@ -1037,65 +1040,64 @@ export const OperatorHubTileView: FC = (props) => { /> {detailsItem && ( - - -
- {!detailsItem.installed ? ( - - {t('olm~Install')} - - ) : ( - - )} -
- - } > - + + + +
+ {!detailsItem.installed ? ( + + {t('olm~Install')} + + ) : ( + + )} +
+
+ + +
)} diff --git a/frontend/packages/operator-lifecycle-manager/src/components/registry-poll-interval-details.tsx b/frontend/packages/operator-lifecycle-manager/src/components/registry-poll-interval-details.tsx index 91175eb6d41..683529d773e 100644 --- a/frontend/packages/operator-lifecycle-manager/src/components/registry-poll-interval-details.tsx +++ b/frontend/packages/operator-lifecycle-manager/src/components/registry-poll-interval-details.tsx @@ -142,7 +142,7 @@ export const RegistryPollIntervalDetailItem: FC - + ); diff --git a/frontend/public/components/modals/configure-machine-autoscaler-modal.tsx b/frontend/public/components/modals/configure-machine-autoscaler-modal.tsx index 447c9a79520..678280097d6 100644 --- a/frontend/public/components/modals/configure-machine-autoscaler-modal.tsx +++ b/frontend/public/components/modals/configure-machine-autoscaler-modal.tsx @@ -8,8 +8,6 @@ import { ModalBody, ModalFooter, Button, - HelperText, - HelperTextItem, FormGroup, Form, } from '@patternfly/react-core'; @@ -20,6 +18,7 @@ import { resourcePathFromModel } from '../utils/resource-link'; import { K8sResourceKind } from '../../module/k8s'; import { k8sCreateResource } from '@console/dynamic-plugin-sdk/src/utils/k8s'; import { usePromiseHandler } from '@console/shared/src/hooks/promise-handler'; +import { ModalErrorContent } from '@console/shared/src/components/modal-error-content'; export const ConfigureMachineAutoscalerModal: OverlayComponent = ({ machineSet, @@ -116,7 +115,7 @@ export const ConfigureMachineAutoscalerModal: OverlayComponent -
+ - {errorMessage && ( - - {errorMessage} - - )}
- - - + ); diff --git a/frontend/public/components/modals/delete-modal.tsx b/frontend/public/components/modals/delete-modal.tsx index d7023fb00c3..69ece70b7e8 100644 --- a/frontend/public/components/modals/delete-modal.tsx +++ b/frontend/public/components/modals/delete-modal.tsx @@ -1,17 +1,21 @@ import * as _ from 'lodash'; import type { ReactNode } from 'react'; import { useState, useCallback, useEffect } from 'react'; -import { Alert, Checkbox } from '@patternfly/react-core'; +import { + Alert, + Button, + Checkbox, + Form, + Modal, + ModalBody, + ModalFooter, + ModalHeader, + ModalVariant, +} from '@patternfly/react-core'; import { Trans, useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom-v5-compat'; import { OverlayComponent } from '@console/dynamic-plugin-sdk/src/app/modal-support/OverlayProvider'; -import { - ModalTitle, - ModalBody, - ModalSubmitFooter, - ModalWrapper, - ModalComponentProps, -} from '../factory/modal'; +import { ModalComponentProps } from '../factory/modal'; import { resourceListPathFromModel, ResourceLink } from '../utils/resource-link'; import { k8sKill, @@ -24,6 +28,7 @@ import { import { YellowExclamationTriangleIcon } from '@console/shared/src/components/status/icons'; import { ClusterServiceVersionModel } from '@console/operator-lifecycle-manager/src/models'; import { findOwner } from '../../module/k8s/managed-by'; +import { ModalErrorContent } from '@console/shared/src/components/modal-error-content'; import { LocationDescriptor } from 'history'; import { usePromiseHandler } from '@console/shared/src/hooks/promise-handler'; @@ -87,93 +92,124 @@ export const DeleteModal = (props: DeleteModalProps) => { }); const { kind, resource, message } = props; + return ( -
- - {' '} - {t('public~Delete {{kind}}?', { - kind: kind ? (kind.labelKey ? t(kind.labelKey) : kind.label) : '', - })} - - - {message} -
- {_.has(resource.metadata, 'namespace') ? ( - - Are you sure you want to delete{' '} - - {{ resourceName: resource?.metadata?.name }} - {' '} - in namespace {{ namespace: resource?.metadata?.namespace }}? - - ) : ( - - Are you sure you want to delete{' '} - - {{ resourceName: resource?.metadata?.name }} - - ? - - )} - {_.has(kind, 'propagationPolicy') && ( - setIsChecked(checked)} - isChecked={isChecked} - name="deleteDependentObjects" - id="deleteDependentObjects" - /> - )} - {props.deleteAllResources && ( - setIsDeleteOtherResourcesChecked(checked)} - isChecked={isDeleteOtherResourcesChecked} - name="deleteOtherResources" - id="deleteOtherResources" - /> - )} - {owner && ( - + <> + + {' '} + {t('public~Delete {{kind}}?', { + kind: kind ? (kind.labelKey ? t(kind.labelKey) : kind.label) : '', + })} + + } + /> + + + {message} +
+ {_.has(resource.metadata, 'namespace') ? ( + + Are you sure you want to delete{' '} + + {{ resourceName: resource?.metadata?.name }} + {' '} + in namespace {{ namespace: resource?.metadata?.namespace }}? + + ) : ( - This resource is managed by{' '} - {' '} - and any modifications may be overwritten. Edit the managing resource to preserve - changes. + Are you sure you want to delete{' '} + + {{ resourceName: resource?.metadata?.name }} + + ? - - )} -
+ )} + {_.has(kind, 'propagationPolicy') && ( + setIsChecked(checked)} + isChecked={isChecked} + name="deleteDependentObjects" + id="deleteDependentObjects" + /> + )} + {props.deleteAllResources && ( + setIsDeleteOtherResourcesChecked(checked)} + isChecked={isDeleteOtherResourcesChecked} + name="deleteOtherResources" + id="deleteOtherResources" + /> + )} + {owner && ( + + + This resource is managed by{' '} + {' '} + and any modifications may be overwritten. Edit the managing resource to preserve + changes. + + + )} +
+
- - + + + + + + ); }; export const DeleteModalOverlay: OverlayComponent = (props) => { - return ( - - - - ); + const [isOpen, setIsOpen] = useState(true); + + // Move focus away from the triggering element to prevent aria-hidden warning + useEffect(() => { + if (document.activeElement instanceof HTMLElement) { + document.activeElement.blur(); + } + }, []); + + const handleClose = () => { + setIsOpen(false); + props.closeOverlay(); + }; + + return isOpen ? ( + + + + ) : null; }; export type DeleteModalProps = { diff --git a/frontend/public/components/modals/delete-namespace-modal.tsx b/frontend/public/components/modals/delete-namespace-modal.tsx index 667b11365fc..1038373ce3e 100644 --- a/frontend/public/components/modals/delete-namespace-modal.tsx +++ b/frontend/public/components/modals/delete-namespace-modal.tsx @@ -120,7 +120,7 @@ export const DeleteNamespaceModal: OverlayComponent = > {t('public~Delete')} - diff --git a/frontend/public/components/secrets/create-secret/SecretFormWrapper.tsx b/frontend/public/components/secrets/create-secret/SecretFormWrapper.tsx index 1a7d50ca467..b0e056d82bc 100644 --- a/frontend/public/components/secrets/create-secret/SecretFormWrapper.tsx +++ b/frontend/public/components/secrets/create-secret/SecretFormWrapper.tsx @@ -205,7 +205,7 @@ export const SecretFormWrapper: FC = (props) => { > {props.saveButtonText || t('public~Create')} -