diff --git a/src/Modal/AlertModal.jsx b/src/Modal/AlertModal.jsx deleted file mode 100644 index 0fc42354e5..0000000000 --- a/src/Modal/AlertModal.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; - -import { requiredWhenNot } from '../utils/propTypes'; -import Icon from '../Icon'; -import ModalDialog from './ModalDialog'; - -function AlertModal({ - children, - footerNode, - icon, - ...props -}) { - return ( - - - - {icon && ( - - )} - {props.title} - - - {children} - {footerNode && {footerNode}} - - ); -} - -AlertModal.propTypes = { - children: PropTypes.node.isRequired, - /** The aria-label of the dialog */ - title: PropTypes.string.isRequired, - /** Is the modal dialog open or closed */ - isOpen: PropTypes.bool, - /** Prevent clicking on the backdrop or pressing Esc to close the modal */ - isBlocking: PropTypes.bool, - /** Specifies whether the dialog box should contain 'x' icon button in the top right */ - hasCloseButton: PropTypes.bool, - /** A callback to close the modal dialog */ - onClose: requiredWhenNot(PropTypes.func, 'isBlocking'), - /** Sizes determine the maximum width of the dialog box */ - size: PropTypes.oneOf(['sm', 'md', 'lg', 'xl', 'fullscreen']), - /** The visual style of the dialog box */ - variant: PropTypes.oneOf(['default', 'warning', 'danger', 'success']), - /** The label supplied to the close icon button if one is rendered */ - closeLabel: PropTypes.string, - /** Specifies class name to append to the base element */ - className: PropTypes.string, - /** - * Determines where a scrollbar should appear if a modal is too large for the - * viewport. When false, the ModalDialog.Body receives a scrollbar, when true - * the browser window itself receives the scrollbar. - */ - isFullscreenScroll: PropTypes.bool, - /** Specifies what should be displayed in the footer of the dialog box */ - footerNode: PropTypes.node, - /** Icon that will be shown in the header of modal */ - icon: PropTypes.elementType, -}; - -AlertModal.defaultProps = { - isOpen: false, - isBlocking: false, - hasCloseButton: false, - onClose: () => {}, - size: 'md', - variant: 'default', - closeLabel: 'Close', - className: undefined, - isFullscreenScroll: false, - footerNode: null, - icon: undefined, -}; - -export default AlertModal; diff --git a/src/Modal/AlertModal.tsx b/src/Modal/AlertModal.tsx new file mode 100644 index 0000000000..3b2b7f84ad --- /dev/null +++ b/src/Modal/AlertModal.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import classNames from 'classnames'; + +import Icon from '../Icon'; +import ModalDialog from './ModalDialog'; + +type ModalDialogProps = React.ComponentProps; + +// Extends all ModalDialog props, but omits certain props to re-declare them: +// - onClose: ModalDialog requires it, but AlertModal defaults it to () => {} +// - isOverflowVisible: required in ModalDialog but was absent from AlertModal's propTypes; +// undefined is falsy so defaulting to false matches the previous behavior +// - variant: ModalDialog allows 'dark' but AlertModal intentionally excludes it +// footerNode and icon are AlertModal-specific props not present on ModalDialog +interface AlertModalProps extends Omit { + /** A callback to close the modal dialog */ + onClose?: () => void; + /** Specifies whether overflow content inside the modal should be visible */ + isOverflowVisible?: boolean; + /** The visual style of the dialog box */ + variant?: 'default' | 'warning' | 'danger' | 'success'; + /** Specifies what should be displayed in the footer of the dialog box */ + footerNode?: React.ReactNode; + /** Icon that will be shown in the header of modal */ + icon?: React.ComponentType; +} + +function AlertModal({ + children, + footerNode = null, + icon, + title, + hasCloseButton = false, + onClose = () => {}, + isOverflowVisible = false, + className, + ...props +}: AlertModalProps) { + return ( + + + + {icon && ( + + )} + {title} + + + {children} + {footerNode && {footerNode}} + + ); +} + +export default AlertModal; diff --git a/src/Modal/tests/AlertModal.test.jsx b/src/Modal/tests/AlertModal.test.jsx index 88cbd3ae5c..03e88e49f6 100644 --- a/src/Modal/tests/AlertModal.test.jsx +++ b/src/Modal/tests/AlertModal.test.jsx @@ -1,5 +1,6 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { IntlProvider } from 'react-intl'; import AlertModal from '../AlertModal'; @@ -55,6 +56,30 @@ describe('', () => { expect(body).toBeInTheDocument(); }); + it('renders without optional props', () => { + render( + + + + + , + ); + expect(screen.getByText('The body of alert.')).toBeInTheDocument(); + expect(screen.queryByText('footer')).not.toBeInTheDocument(); + }); + + it('close button click invokes default no-op onClose without error', async () => { + render( + + + + + , + ); + await userEvent.click(screen.getByRole('button', { name: 'Close' })); + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + describe('with variant prop', () => { it('renders warning variant', () => { render( diff --git a/src/index.ts b/src/index.ts index 7e11e26a8c..f5b56b7ab3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ // // // // // // // // // // // // // // // // // // // // // // // // // // // export { default as ActionRow } from './ActionRow'; export { default as Alert, ALERT_CLOSE_LABEL_TEXT } from './Alert'; +export { default as AlertModal } from './Modal/AlertModal'; export { default as Annotation } from './Annotation'; export { default as Avatar } from './Avatar'; export { default as AvatarButton } from './AvatarButton'; @@ -117,8 +118,6 @@ export { default as MarketingModal } from './Modal/MarketingModal'; // @ts-ignore: has yet to be converted to TypeScript export { default as StandardModal, STANDARD_MODAL_CLOSE_LABEL } from './Modal/StandardModal'; // @ts-ignore: has yet to be converted to TypeScript -export { default as AlertModal } from './Modal/AlertModal'; -// @ts-ignore: has yet to be converted to TypeScript export { default as ModalPopup } from './Modal/ModalPopup'; // @ts-ignore: has yet to be converted to TypeScript export { default as PopperElement } from './Modal/PopperElement';