Skip to content

Commit

Permalink
feat(Modal): promoted next version (#10358)
Browse files Browse the repository at this point in the history
* feat(Modal): promoted next version

* Added deprecated flag
  • Loading branch information
thatblindgeye authored Jun 6, 2024
1 parent c78a547 commit 3fabe0e
Show file tree
Hide file tree
Showing 115 changed files with 3,019 additions and 2,984 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AboutModalBoxHeader } from './AboutModalBoxHeader';
import { AboutModalBoxBrand } from './AboutModalBoxBrand';
import { AboutModalBoxCloseButton } from './AboutModalBoxCloseButton';
import { AboutModalBox } from './AboutModalBox';
import { Modal, ModalVariant } from '../Modal';
import { Modal, ModalVariant } from '../../deprecated/components/Modal';
import { GenerateId } from '../../helpers/GenerateId/GenerateId';

export interface AboutModalProps extends React.HTMLProps<HTMLDivElement> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import React from 'react';
import {
LabelGroup,
Label,
Modal,
ModalVariant,
Button,
Form,
FormGroup,
Expand All @@ -16,6 +14,7 @@ import {
Radio,
Popper
} from '@patternfly/react-core';
import { Modal as ModalDeprecated, ModalVariant as ModalVariantDeprecated } from '@patternfly/react-core/deprecated';
import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-icon';

export const LabelGroupEditableAddModal: React.FunctionComponent = () => {
Expand Down Expand Up @@ -255,8 +254,8 @@ export const LabelGroupEditableAddModal: React.FunctionComponent = () => {
</Label>
))}
</LabelGroup>
<Modal
variant={ModalVariant.small}
<ModalDeprecated
variant={ModalVariantDeprecated.small}
title="Add Label"
isOpen={isModalOpen}
onClose={handleModalToggle}
Expand Down Expand Up @@ -359,7 +358,7 @@ export const LabelGroupEditableAddModal: React.FunctionComponent = () => {
/>
</FormGroup>
</Form>
</Modal>
</ModalDeprecated>
</div>
);
};
91 changes: 9 additions & 82 deletions packages/react-core/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,53 +7,29 @@ import { ModalContent } from './ModalContent';
import { OUIAProps, getDefaultOUIAId } from '../../helpers';

export interface ModalProps extends React.HTMLProps<HTMLDivElement>, OUIAProps {
/** Action buttons to add to the standard modal footer. Ignored if the footer property
* is passed in.
*/
actions?: any;
/** The parent container to append the modal to. Defaults to "document.body". */
appendTo?: HTMLElement | (() => HTMLElement);
/** Id to use for the modal box descriptor. */
/** Id to use for the modal box description. This should match the ModalHeader labelId or descriptorId. */
'aria-describedby'?: string;
/** Accessible descriptor of the modal. */
/** Adds an accessible name to the modal when there is no title in the ModalHeader. */
'aria-label'?: string;
/** Id to use for the modal box label. */
/** Id to use for the modal box label. This should include the ModalHeader labelId. */
'aria-labelledby'?: string;
/** Accessible label applied to the modal box body. This should be used to communicate
* important information about the modal box body div element if needed, such as that it
* is scrollable.
*/
bodyAriaLabel?: string;
/** Accessible role applied to the modal box body. This will default to "region" if the
* bodyAriaLabel property is passed in. Set to a more appropriate role as applicable
* based on the modal content and context.
*/
bodyAriaRole?: string;
/** Content rendered inside the modal. */
children: React.ReactNode;
/** Additional classes added to the modal. */
className?: string;
/** Description of the modal. */
description?: React.ReactNode;
/** Flag to disable focus trap. */
disableFocusTrap?: boolean;
/** The element to focus when the modal opens. By default the first
* focusable element will receive focus.
*/
elementToFocus?: HTMLElement | SVGElement | string;
/** Custom footer. */
footer?: React.ReactNode;
/** Flag indicating if modal content should be placed in a modal box body wrapper. */
hasNoBodyWrapper?: boolean;
/** Complex header (more than just text), supersedes the title property for header content. */
header?: React.ReactNode;
/** Optional help section for the modal header. */
help?: React.ReactNode;
/** An id to use for the modal box container. */
id?: string;
/** Flag to show the modal. */
isOpen?: boolean;
/** A callback for when the close button is clicked. */
/** Add callback for when the close button is clicked. This prop needs to be passed to render the close button */
onClose?: (event: KeyboardEvent | React.MouseEvent) => void;
/** Modal handles pressing of the escape key and closes the modal. If you want to handle
* this yourself you can use this callback function. */
Expand All @@ -62,16 +38,6 @@ export interface ModalProps extends React.HTMLProps<HTMLDivElement>, OUIAProps {
position?: 'default' | 'top';
/** Offset from alternate position. Can be any valid CSS length/percentage. */
positionOffset?: string;
/** Flag to show the close button in the header area of the modal. */
showClose?: boolean;
/** Simple text content of the modal header. Also used for the aria-label on the body. */
title?: string;
/** Optional alert icon (or other) to show before the title of the modal header. When the
* predefined alert types are used the default styling will be automatically applied.
*/
titleIconVariant?: 'success' | 'danger' | 'warning' | 'info' | 'custom' | React.ComponentType<any>;
/** Optional title label text for screen readers. */
titleLabel?: string;
/** Variant of the modal. */
variant?: 'small' | 'medium' | 'large' | 'default';
/** Default width of the modal. */
Expand Down Expand Up @@ -100,24 +66,10 @@ class Modal extends React.Component<ModalProps, ModalState> {
static displayName = 'Modal';
static currentId = 0;
boxId = '';
labelId = '';
descriptorId = '';

static defaultProps: PickOptional<ModalProps> = {
className: '',
isOpen: false,
title: '',
titleIconVariant: null,
titleLabel: '',
'aria-label': '',
showClose: true,
'aria-describedby': '',
'aria-labelledby': '',
id: undefined,
actions: [] as any[],
onClose: () => undefined as any,
variant: 'default',
hasNoBodyWrapper: false,
appendTo: () => document.body,
ouiaSafe: true,
position: 'default'
Expand All @@ -126,11 +78,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
constructor(props: ModalProps) {
super(props);
const boxIdNum = Modal.currentId++;
const labelIdNum = boxIdNum + 1;
const descriptorIdNum = boxIdNum + 2;
this.boxId = props.id || `pf-modal-part-${boxIdNum}`;
this.labelId = `pf-modal-part-${labelIdNum}`;
this.descriptorId = `pf-modal-part-${descriptorIdNum}`;

this.state = {
container: undefined,
Expand Down Expand Up @@ -168,11 +116,9 @@ class Modal extends React.Component<ModalProps, ModalState> {
componentDidMount() {
const {
appendTo,
title,
'aria-describedby': ariaDescribedby,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
hasNoBodyWrapper,
header
'aria-labelledby': ariaLabelledby
} = this.props;
const target: HTMLElement = this.getElement(appendTo);
const container = document.createElement('div');
Expand All @@ -186,16 +132,9 @@ class Modal extends React.Component<ModalProps, ModalState> {
target.classList.remove(css(styles.backdropOpen));
}

if (this.isEmpty(title) && this.isEmpty(ariaLabel) && this.isEmpty(ariaLabelledby)) {
if (!ariaDescribedby && !ariaLabel && !ariaLabelledby) {
// eslint-disable-next-line no-console
console.error('Modal: Specify at least one of: title, aria-label, aria-labelledby.');
}

if (this.isEmpty(ariaLabel) && this.isEmpty(ariaLabelledby) && (hasNoBodyWrapper || header)) {
// eslint-disable-next-line no-console
console.error(
'Modal: When using hasNoBodyWrapper or setting a custom header, ensure you assign an accessible name to the the modal container with aria-label or aria-labelledby.'
);
console.error('Modal: Specify at least one of: aria-describedby, aria-label, aria-labelledby.');
}
}

Expand Down Expand Up @@ -231,11 +170,6 @@ class Modal extends React.Component<ModalProps, ModalState> {
'aria-labelledby': ariaLabelledby,
'aria-label': ariaLabel,
'aria-describedby': ariaDescribedby,
bodyAriaLabel,
bodyAriaRole,
title,
titleIconVariant,
titleLabel,
ouiaId,
ouiaSafe,
position,
Expand All @@ -250,22 +184,15 @@ class Modal extends React.Component<ModalProps, ModalState> {

return ReactDOM.createPortal(
<ModalContent
{...props}
boxId={this.boxId}
labelId={this.labelId}
descriptorId={this.descriptorId}
title={title}
titleIconVariant={titleIconVariant}
titleLabel={titleLabel}
aria-label={ariaLabel}
aria-describedby={ariaDescribedby}
aria-labelledby={ariaLabelledby}
bodyAriaLabel={bodyAriaLabel}
bodyAriaRole={bodyAriaRole}
ouiaId={ouiaId !== undefined ? ouiaId : this.state.ouiaStateId}
ouiaSafe={ouiaSafe}
position={position}
elementToFocus={elementToFocus}
{...props}
/>,
container
) as React.ReactElement;
Expand Down
12 changes: 6 additions & 6 deletions packages/react-core/src/components/Modal/ModalBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import styles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
import topSpacer from '@patternfly/react-tokens/dist/esm/c_modal_box_m_align_top_spacer';

export interface ModalBoxProps extends React.HTMLProps<HTMLDivElement> {
/** Id to use for the modal box description. */
'aria-describedby': string;
/** Accessible descriptor of the modal. */
/** Id to use for the modal box description. This should match the ModalHeader labelId or descriptorId */
'aria-describedby'?: string;
/** Adds an accessible name to the modal when there is no title in the ModalHeader. */
'aria-label'?: string;
/** Id to use for the modal box label. */
'aria-labelledby'?: string;
Expand All @@ -24,12 +24,12 @@ export interface ModalBoxProps extends React.HTMLProps<HTMLDivElement> {

export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
children,
className = '',
className,
variant = 'default',
position,
positionOffset,
'aria-labelledby': ariaLabelledby,
'aria-label': ariaLabel = '',
'aria-label': ariaLabel,
'aria-describedby': ariaDescribedby,
style,
...props
Expand All @@ -40,7 +40,6 @@ export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
}
return (
<div
{...props}
role="dialog"
aria-label={ariaLabel || null}
aria-labelledby={ariaLabelledby || null}
Expand All @@ -55,6 +54,7 @@ export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
variant === 'medium' && styles.modifiers.md
)}
style={style}
{...props}
>
{children}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface ModalBoxCloseButtonProps extends OUIAProps {

export const ModalBoxCloseButton: React.FunctionComponent<ModalBoxCloseButtonProps> = ({
className,
onClose = () => undefined as any,
onClose,
'aria-label': ariaLabel = 'Close',
ouiaId,
...props
Expand Down
12 changes: 7 additions & 5 deletions packages/react-core/src/components/Modal/ModalBoxTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,29 @@ export interface ModalBoxTitleProps {
/** Additional classes added to the modal box title. */
className?: string;
/** Id of the modal box title. */
id: string;
id?: string;
/** Content rendered inside the modal box title. */
title: React.ReactNode;
/** Optional alert icon (or other) to show before the title. When the predefined alert types
* are used the default styling will be automatically applied. */
titleIconVariant?: 'success' | 'danger' | 'warning' | 'info' | 'custom' | React.ComponentType<any>;
/** Optional title label text for screen readers. */
titleLabel?: string;
titleScreenReaderText?: string;
}

export const ModalBoxTitle: React.FunctionComponent<ModalBoxTitleProps> = ({
className = '',
className,
id,
title,
titleIconVariant,
titleLabel = '',
titleScreenReaderText,
...props
}: ModalBoxTitleProps) => {
const [hasTooltip, setHasTooltip] = React.useState(false);
const h1 = React.useRef<HTMLHeadingElement>(null);
const label = titleLabel || (isVariantIcon(titleIconVariant) ? `${capitalize(titleIconVariant)} alert:` : titleLabel);
const label =
titleScreenReaderText ||
(isVariantIcon(titleIconVariant) ? `${capitalize(titleIconVariant)} alert:` : titleScreenReaderText);
const variantIcons = {
success: <CheckCircleIcon />,
danger: <ExclamationCircleIcon />,
Expand Down
Loading

0 comments on commit 3fabe0e

Please sign in to comment.