@patternfly/react-core 5.2.0-prerelease.40 → 5.2.0-prerelease.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/components/package.json +1 -1
- package/deprecated/package.json +1 -1
- package/dist/dynamic/components/AboutModal/package.json +1 -1
- package/dist/dynamic/components/Accordion/package.json +1 -1
- package/dist/dynamic/components/ActionList/package.json +1 -1
- package/dist/dynamic/components/Alert/package.json +1 -1
- package/dist/dynamic/components/Avatar/package.json +1 -1
- package/dist/dynamic/components/BackToTop/package.json +1 -1
- package/dist/dynamic/components/Backdrop/package.json +1 -1
- package/dist/dynamic/components/BackgroundImage/package.json +1 -1
- package/dist/dynamic/components/Badge/package.json +1 -1
- package/dist/dynamic/components/Banner/package.json +1 -1
- package/dist/dynamic/components/Brand/package.json +1 -1
- package/dist/dynamic/components/Breadcrumb/package.json +1 -1
- package/dist/dynamic/components/Button/package.json +1 -1
- package/dist/dynamic/components/CalendarMonth/package.json +1 -1
- package/dist/dynamic/components/Card/package.json +1 -1
- package/dist/dynamic/components/Checkbox/package.json +1 -1
- package/dist/dynamic/components/Chip/package.json +1 -1
- package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
- package/dist/dynamic/components/CodeBlock/package.json +1 -1
- package/dist/dynamic/components/DataList/package.json +1 -1
- package/dist/dynamic/components/DatePicker/package.json +1 -1
- package/dist/dynamic/components/DescriptionList/package.json +1 -1
- package/dist/dynamic/components/Divider/package.json +1 -1
- package/dist/dynamic/components/DragDrop/package.json +1 -1
- package/dist/dynamic/components/Drawer/package.json +1 -1
- package/dist/dynamic/components/Dropdown/package.json +1 -1
- package/dist/dynamic/components/DualListSelector/package.json +1 -1
- package/dist/dynamic/components/EmptyState/package.json +1 -1
- package/dist/dynamic/components/ExpandableSection/package.json +1 -1
- package/dist/dynamic/components/FileUpload/package.json +1 -1
- package/dist/dynamic/components/Form/package.json +1 -1
- package/dist/dynamic/components/FormSelect/package.json +1 -1
- package/dist/dynamic/components/HelperText/package.json +1 -1
- package/dist/dynamic/components/Hint/package.json +1 -1
- package/dist/dynamic/components/Icon/package.json +1 -1
- package/dist/dynamic/components/InputGroup/package.json +1 -1
- package/dist/dynamic/components/JumpLinks/package.json +1 -1
- package/dist/dynamic/components/Label/package.json +1 -1
- package/dist/dynamic/components/List/package.json +1 -1
- package/dist/dynamic/components/LoginPage/package.json +1 -1
- package/dist/dynamic/components/Masthead/package.json +1 -1
- package/dist/dynamic/components/Menu/package.json +1 -1
- package/dist/dynamic/components/MenuToggle/package.json +1 -1
- package/dist/dynamic/components/Modal/package.json +1 -1
- package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
- package/dist/dynamic/components/Nav/package.json +1 -1
- package/dist/dynamic/components/NotificationBadge/package.json +1 -1
- package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
- package/dist/dynamic/components/NumberInput/package.json +1 -1
- package/dist/dynamic/components/OverflowMenu/package.json +1 -1
- package/dist/dynamic/components/Page/package.json +1 -1
- package/dist/dynamic/components/Pagination/package.json +1 -1
- package/dist/dynamic/components/Panel/package.json +1 -1
- package/dist/dynamic/components/Popover/package.json +1 -1
- package/dist/dynamic/components/Progress/package.json +1 -1
- package/dist/dynamic/components/ProgressStepper/package.json +1 -1
- package/dist/dynamic/components/Radio/package.json +1 -1
- package/dist/dynamic/components/SearchInput/package.json +1 -1
- package/dist/dynamic/components/Select/package.json +1 -1
- package/dist/dynamic/components/Sidebar/package.json +1 -1
- package/dist/dynamic/components/SimpleList/package.json +1 -1
- package/dist/dynamic/components/Skeleton/package.json +1 -1
- package/dist/dynamic/components/SkipToContent/package.json +1 -1
- package/dist/dynamic/components/Slider/package.json +1 -1
- package/dist/dynamic/components/Spinner/package.json +1 -1
- package/dist/dynamic/components/Switch/package.json +1 -1
- package/dist/dynamic/components/Tabs/package.json +1 -1
- package/dist/dynamic/components/Text/package.json +1 -1
- package/dist/dynamic/components/TextArea/package.json +1 -1
- package/dist/dynamic/components/TextInput/package.json +1 -1
- package/dist/dynamic/components/TextInputGroup/package.json +1 -1
- package/dist/dynamic/components/Tile/package.json +1 -1
- package/dist/dynamic/components/TimePicker/package.json +1 -1
- package/dist/dynamic/components/Timestamp/package.json +1 -1
- package/dist/dynamic/components/Title/package.json +1 -1
- package/dist/dynamic/components/ToggleGroup/package.json +1 -1
- package/dist/dynamic/components/Toolbar/package.json +1 -1
- package/dist/dynamic/components/Tooltip/package.json +1 -1
- package/dist/dynamic/components/TreeView/package.json +1 -1
- package/dist/dynamic/components/Truncate/package.json +1 -1
- package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
- package/dist/dynamic/components/Wizard/package.json +1 -1
- package/dist/dynamic/deprecated/components/ApplicationLauncher/package.json +1 -1
- package/dist/dynamic/deprecated/components/ContextSelector/package.json +1 -1
- package/dist/dynamic/deprecated/components/Dropdown/package.json +1 -1
- package/dist/dynamic/deprecated/components/OptionsMenu/package.json +1 -1
- package/dist/dynamic/deprecated/components/PageHeader/package.json +1 -1
- package/dist/dynamic/deprecated/components/Select/package.json +1 -1
- package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
- package/dist/dynamic/deprecated/components/package.json +1 -1
- package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
- package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
- package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
- package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
- package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
- package/dist/dynamic/helpers/constants/package.json +1 -1
- package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
- package/dist/dynamic/helpers/favorites/package.json +1 -1
- package/dist/dynamic/helpers/fileUtils/package.json +1 -1
- package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
- package/dist/dynamic/helpers/package.json +1 -1
- package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
- package/dist/dynamic/helpers/typeUtils/package.json +1 -1
- package/dist/dynamic/helpers/useInterval/package.json +1 -1
- package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
- package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
- package/dist/dynamic/helpers/util/package.json +1 -1
- package/dist/dynamic/layouts/Bullseye/package.json +1 -1
- package/dist/dynamic/layouts/Flex/package.json +1 -1
- package/dist/dynamic/layouts/Gallery/package.json +1 -1
- package/dist/dynamic/layouts/Grid/package.json +1 -1
- package/dist/dynamic/layouts/Level/package.json +1 -1
- package/dist/dynamic/layouts/Split/package.json +1 -1
- package/dist/dynamic/layouts/Stack/package.json +1 -1
- package/dist/dynamic/next/components/Modal/package.json +1 -0
- package/dist/dynamic/next/components/package.json +1 -1
- package/dist/esm/next/components/Modal/Modal.d.ts +73 -0
- package/dist/esm/next/components/Modal/Modal.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/Modal.js +114 -0
- package/dist/esm/next/components/Modal/Modal.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalBody.d.ts +19 -0
- package/dist/esm/next/components/Modal/ModalBody.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalBody.js +11 -0
- package/dist/esm/next/components/Modal/ModalBody.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalBox.d.ts +21 -0
- package/dist/esm/next/components/Modal/ModalBox.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalBox.js +15 -0
- package/dist/esm/next/components/Modal/ModalBox.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalBoxCloseButton.d.ts +14 -0
- package/dist/esm/next/components/Modal/ModalBoxCloseButton.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalBoxCloseButton.js +14 -0
- package/dist/esm/next/components/Modal/ModalBoxCloseButton.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalBoxDescription.d.ts +11 -0
- package/dist/esm/next/components/Modal/ModalBoxDescription.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalBoxDescription.js +10 -0
- package/dist/esm/next/components/Modal/ModalBoxDescription.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalBoxTitle.d.ts +17 -0
- package/dist/esm/next/components/Modal/ModalBoxTitle.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalBoxTitle.js +38 -0
- package/dist/esm/next/components/Modal/ModalBoxTitle.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalContent.d.ts +42 -0
- package/dist/esm/next/components/Modal/ModalContent.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalContent.js +45 -0
- package/dist/esm/next/components/Modal/ModalContent.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalFooter.d.ts +10 -0
- package/dist/esm/next/components/Modal/ModalFooter.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalFooter.js +10 -0
- package/dist/esm/next/components/Modal/ModalFooter.js.map +1 -0
- package/dist/esm/next/components/Modal/ModalHeader.d.ts +25 -0
- package/dist/esm/next/components/Modal/ModalHeader.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/ModalHeader.js +21 -0
- package/dist/esm/next/components/Modal/ModalHeader.js.map +1 -0
- package/dist/esm/next/components/Modal/index.d.ts +5 -0
- package/dist/esm/next/components/Modal/index.d.ts.map +1 -0
- package/dist/esm/next/components/Modal/index.js +5 -0
- package/dist/esm/next/components/Modal/index.js.map +1 -0
- package/dist/esm/next/components/index.d.ts +1 -1
- package/dist/esm/next/components/index.d.ts.map +1 -1
- package/dist/esm/next/components/index.js +1 -1
- package/dist/esm/next/components/index.js.map +1 -1
- package/dist/js/next/components/Modal/Modal.d.ts +73 -0
- package/dist/js/next/components/Modal/Modal.d.ts.map +1 -0
- package/dist/js/next/components/Modal/Modal.js +117 -0
- package/dist/js/next/components/Modal/Modal.js.map +1 -0
- package/dist/js/next/components/Modal/ModalBody.d.ts +19 -0
- package/dist/js/next/components/Modal/ModalBody.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalBody.js +15 -0
- package/dist/js/next/components/Modal/ModalBody.js.map +1 -0
- package/dist/js/next/components/Modal/ModalBox.d.ts +21 -0
- package/dist/js/next/components/Modal/ModalBox.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalBox.js +19 -0
- package/dist/js/next/components/Modal/ModalBox.js.map +1 -0
- package/dist/js/next/components/Modal/ModalBoxCloseButton.d.ts +14 -0
- package/dist/js/next/components/Modal/ModalBoxCloseButton.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalBoxCloseButton.js +18 -0
- package/dist/js/next/components/Modal/ModalBoxCloseButton.js.map +1 -0
- package/dist/js/next/components/Modal/ModalBoxDescription.d.ts +11 -0
- package/dist/js/next/components/Modal/ModalBoxDescription.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalBoxDescription.js +14 -0
- package/dist/js/next/components/Modal/ModalBoxDescription.js.map +1 -0
- package/dist/js/next/components/Modal/ModalBoxTitle.d.ts +17 -0
- package/dist/js/next/components/Modal/ModalBoxTitle.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalBoxTitle.js +43 -0
- package/dist/js/next/components/Modal/ModalBoxTitle.js.map +1 -0
- package/dist/js/next/components/Modal/ModalContent.d.ts +42 -0
- package/dist/js/next/components/Modal/ModalContent.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalContent.js +49 -0
- package/dist/js/next/components/Modal/ModalContent.js.map +1 -0
- package/dist/js/next/components/Modal/ModalFooter.d.ts +10 -0
- package/dist/js/next/components/Modal/ModalFooter.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalFooter.js +14 -0
- package/dist/js/next/components/Modal/ModalFooter.js.map +1 -0
- package/dist/js/next/components/Modal/ModalHeader.d.ts +25 -0
- package/dist/js/next/components/Modal/ModalHeader.d.ts.map +1 -0
- package/dist/js/next/components/Modal/ModalHeader.js +25 -0
- package/dist/js/next/components/Modal/ModalHeader.js.map +1 -0
- package/dist/js/next/components/Modal/index.d.ts +5 -0
- package/dist/js/next/components/Modal/index.d.ts.map +1 -0
- package/dist/js/next/components/Modal/index.js +8 -0
- package/dist/js/next/components/Modal/index.js.map +1 -0
- package/dist/js/next/components/index.d.ts +1 -1
- package/dist/js/next/components/index.d.ts.map +1 -1
- package/dist/js/next/components/index.js +1 -1
- package/dist/js/next/components/index.js.map +1 -1
- package/helpers/package.json +1 -1
- package/layouts/package.json +1 -1
- package/next/package.json +1 -1
- package/package.json +2 -2
- package/src/next/components/Modal/Modal.tsx +202 -0
- package/src/next/components/Modal/ModalBody.tsx +42 -0
- package/src/next/components/Modal/ModalBox.tsx +63 -0
- package/src/next/components/Modal/ModalBoxCloseButton.tsx +38 -0
- package/src/next/components/Modal/ModalBoxDescription.tsx +24 -0
- package/src/next/components/Modal/ModalBoxTitle.tsx +75 -0
- package/src/next/components/Modal/ModalContent.tsx +131 -0
- package/src/next/components/Modal/ModalFooter.tsx +23 -0
- package/src/next/components/Modal/ModalHeader.tsx +71 -0
- package/src/next/components/Modal/__tests__/Modal.test.tsx +145 -0
- package/src/next/components/Modal/__tests__/ModalBody.test.tsx +60 -0
- package/src/next/components/Modal/__tests__/ModalBox.test.tsx +58 -0
- package/src/next/components/Modal/__tests__/ModalBoxCloseButton.test.tsx +18 -0
- package/src/next/components/Modal/__tests__/ModalBoxDescription.test.tsx +8 -0
- package/src/next/components/Modal/__tests__/ModalBoxHeader.test.tsx +21 -0
- package/src/next/components/Modal/__tests__/ModalBoxTitle.test.tsx +37 -0
- package/src/next/components/Modal/__tests__/ModalContent.test.tsx +46 -0
- package/src/next/components/Modal/__tests__/ModalFooter.test.tsx +8 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalBody.test.tsx.snap +12 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalBox.test.tsx.snap +86 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalBoxDescription.test.tsx.snap +12 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalBoxHeader.test.tsx.snap +46 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalBoxTitle.test.tsx.snap +218 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalContent.test.tsx.snap +128 -0
- package/src/next/components/Modal/__tests__/__snapshots__/ModalFooter.test.tsx.snap +11 -0
- package/src/next/components/Modal/examples/Modal.md +148 -0
- package/src/next/components/Modal/examples/ModalBasic.tsx +43 -0
- package/src/next/components/Modal/examples/ModalCustomFocus.tsx +43 -0
- package/src/next/components/Modal/examples/ModalCustomHeader.tsx +58 -0
- package/src/next/components/Modal/examples/ModalCustomTitleIcon.tsx +54 -0
- package/src/next/components/Modal/examples/ModalCustomWidth.tsx +43 -0
- package/src/next/components/Modal/examples/ModalNoHeaderFooter.tsx +40 -0
- package/src/next/components/Modal/examples/ModalSize.tsx +63 -0
- package/src/next/components/Modal/examples/ModalTitleIcon.tsx +48 -0
- package/src/next/components/Modal/examples/ModalTopAligned.tsx +43 -0
- package/src/next/components/Modal/examples/ModalWithDescription.tsx +80 -0
- package/src/next/components/Modal/examples/ModalWithDropdown.tsx +105 -0
- package/src/next/components/Modal/examples/ModalWithForm.tsx +211 -0
- package/src/next/components/Modal/examples/ModalWithHelp.tsx +63 -0
- package/src/next/components/Modal/examples/ModalWithOverflowingContent.tsx +75 -0
- package/src/next/components/Modal/examples/ModalWithWizard.tsx +59 -0
- package/src/next/components/Modal/index.ts +4 -0
- package/src/next/components/index.ts +1 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { css } from '@patternfly/react-styles';
|
|
3
|
+
import styles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
|
|
4
|
+
import topSpacer from '@patternfly/react-tokens/dist/esm/c_modal_box_m_align_top_spacer';
|
|
5
|
+
|
|
6
|
+
export interface ModalBoxProps extends React.HTMLProps<HTMLDivElement> {
|
|
7
|
+
/** Id to use for the modal box description. This should match the ModalHeader labelId or descriptorId */
|
|
8
|
+
'aria-describedby'?: string;
|
|
9
|
+
/** Adds an accessible name to the modal when there is no title in the ModalHeader. */
|
|
10
|
+
'aria-label'?: string;
|
|
11
|
+
/** Id to use for the modal box label. */
|
|
12
|
+
'aria-labelledby'?: string;
|
|
13
|
+
/** Content rendered inside the modal box. */
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
/** Additional classes added to the modal box. */
|
|
16
|
+
className?: string;
|
|
17
|
+
/** Position of the modal. By default a modal will be positioned vertically and horizontally centered. */
|
|
18
|
+
position?: 'default' | 'top';
|
|
19
|
+
/** Offset from alternate position. Can be any valid CSS length/percentage. */
|
|
20
|
+
positionOffset?: string;
|
|
21
|
+
/** Variant of the modal. */
|
|
22
|
+
variant?: 'small' | 'medium' | 'large' | 'default';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
|
|
26
|
+
children,
|
|
27
|
+
className,
|
|
28
|
+
variant = 'default',
|
|
29
|
+
position,
|
|
30
|
+
positionOffset,
|
|
31
|
+
'aria-labelledby': ariaLabelledby,
|
|
32
|
+
'aria-label': ariaLabel,
|
|
33
|
+
'aria-describedby': ariaDescribedby,
|
|
34
|
+
style,
|
|
35
|
+
...props
|
|
36
|
+
}: ModalBoxProps) => {
|
|
37
|
+
if (positionOffset) {
|
|
38
|
+
style = style || {};
|
|
39
|
+
(style as any)[topSpacer.name] = positionOffset;
|
|
40
|
+
}
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
role="dialog"
|
|
44
|
+
aria-label={ariaLabel || null}
|
|
45
|
+
aria-labelledby={ariaLabelledby || null}
|
|
46
|
+
aria-describedby={ariaDescribedby}
|
|
47
|
+
aria-modal="true"
|
|
48
|
+
className={css(
|
|
49
|
+
styles.modalBox,
|
|
50
|
+
className,
|
|
51
|
+
position === 'top' && styles.modifiers.alignTop,
|
|
52
|
+
variant === 'large' && styles.modifiers.lg,
|
|
53
|
+
variant === 'small' && styles.modifiers.sm,
|
|
54
|
+
variant === 'medium' && styles.modifiers.md
|
|
55
|
+
)}
|
|
56
|
+
style={style}
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
{children}
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
ModalBox.displayName = 'ModalBox';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { css } from '@patternfly/react-styles';
|
|
3
|
+
import styles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
|
|
4
|
+
import { Button } from '../../../components/Button';
|
|
5
|
+
import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
|
|
6
|
+
import { OUIAProps } from '../../../helpers';
|
|
7
|
+
|
|
8
|
+
export interface ModalBoxCloseButtonProps extends OUIAProps {
|
|
9
|
+
/** Additional classes added to the close button. */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** A callback for when the close button is clicked. */
|
|
12
|
+
onClose?: (event: KeyboardEvent | React.MouseEvent) => void;
|
|
13
|
+
/** Accessible descriptor of the close button. */
|
|
14
|
+
'aria-label'?: string;
|
|
15
|
+
/** Value to set the data-ouia-component-id.*/
|
|
16
|
+
ouiaId?: number | string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const ModalBoxCloseButton: React.FunctionComponent<ModalBoxCloseButtonProps> = ({
|
|
20
|
+
className,
|
|
21
|
+
onClose,
|
|
22
|
+
'aria-label': ariaLabel = 'Close',
|
|
23
|
+
ouiaId,
|
|
24
|
+
...props
|
|
25
|
+
}: ModalBoxCloseButtonProps) => (
|
|
26
|
+
<div className={css(styles.modalBoxClose, className)}>
|
|
27
|
+
<Button
|
|
28
|
+
variant="plain"
|
|
29
|
+
onClick={(event) => onClose(event)}
|
|
30
|
+
aria-label={ariaLabel}
|
|
31
|
+
{...(ouiaId && { ouiaId: `${ouiaId}-${ModalBoxCloseButton.displayName}` })}
|
|
32
|
+
{...props}
|
|
33
|
+
>
|
|
34
|
+
<TimesIcon />
|
|
35
|
+
</Button>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
ModalBoxCloseButton.displayName = 'ModalBoxCloseButton';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { css } from '@patternfly/react-styles';
|
|
3
|
+
import styles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
|
|
4
|
+
|
|
5
|
+
export interface ModalBoxDescriptionProps {
|
|
6
|
+
/** Content rendered inside the description. */
|
|
7
|
+
children?: React.ReactNode;
|
|
8
|
+
/** Additional classes added to the description. */
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Id of the description. */
|
|
11
|
+
id?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ModalBoxDescription: React.FunctionComponent<ModalBoxDescriptionProps> = ({
|
|
15
|
+
children = null,
|
|
16
|
+
className = '',
|
|
17
|
+
id = '',
|
|
18
|
+
...props
|
|
19
|
+
}: ModalBoxDescriptionProps) => (
|
|
20
|
+
<div {...props} id={id} className={css(styles.modalBoxDescription, className)}>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
ModalBoxDescription.displayName = 'ModalBoxDescription';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import modalStyles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
|
|
3
|
+
import { css } from '@patternfly/react-styles';
|
|
4
|
+
import { capitalize } from '../../../helpers';
|
|
5
|
+
import { Tooltip } from '../../../components/Tooltip';
|
|
6
|
+
import CheckCircleIcon from '@patternfly/react-icons/dist/esm/icons/check-circle-icon';
|
|
7
|
+
import ExclamationCircleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon';
|
|
8
|
+
import ExclamationTriangleIcon from '@patternfly/react-icons/dist/esm/icons/exclamation-triangle-icon';
|
|
9
|
+
import InfoCircleIcon from '@patternfly/react-icons/dist/esm/icons/info-circle-icon';
|
|
10
|
+
import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
|
|
11
|
+
import { useIsomorphicLayoutEffect } from '../../../helpers';
|
|
12
|
+
|
|
13
|
+
export const isVariantIcon = (icon: any): icon is string =>
|
|
14
|
+
['success', 'danger', 'warning', 'info', 'custom'].includes(icon as string);
|
|
15
|
+
|
|
16
|
+
export interface ModalBoxTitleProps {
|
|
17
|
+
/** Additional classes added to the modal box title. */
|
|
18
|
+
className?: string;
|
|
19
|
+
/** Id of the modal box title. */
|
|
20
|
+
id?: string;
|
|
21
|
+
/** Content rendered inside the modal box title. */
|
|
22
|
+
title: React.ReactNode;
|
|
23
|
+
/** Optional alert icon (or other) to show before the title. When the predefined alert types
|
|
24
|
+
* are used the default styling will be automatically applied. */
|
|
25
|
+
titleIconVariant?: 'success' | 'danger' | 'warning' | 'info' | 'custom' | React.ComponentType<any>;
|
|
26
|
+
/** Optional title label text for screen readers. */
|
|
27
|
+
titleScreenReaderText?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const ModalBoxTitle: React.FunctionComponent<ModalBoxTitleProps> = ({
|
|
31
|
+
className,
|
|
32
|
+
id,
|
|
33
|
+
title,
|
|
34
|
+
titleIconVariant,
|
|
35
|
+
titleScreenReaderText,
|
|
36
|
+
...props
|
|
37
|
+
}: ModalBoxTitleProps) => {
|
|
38
|
+
const [hasTooltip, setHasTooltip] = React.useState(false);
|
|
39
|
+
const h1 = React.useRef<HTMLHeadingElement>(null);
|
|
40
|
+
const label =
|
|
41
|
+
titleScreenReaderText ||
|
|
42
|
+
(isVariantIcon(titleIconVariant) ? `${capitalize(titleIconVariant)} alert:` : titleScreenReaderText);
|
|
43
|
+
const variantIcons = {
|
|
44
|
+
success: <CheckCircleIcon />,
|
|
45
|
+
danger: <ExclamationCircleIcon />,
|
|
46
|
+
warning: <ExclamationTriangleIcon />,
|
|
47
|
+
info: <InfoCircleIcon />,
|
|
48
|
+
custom: <BellIcon />
|
|
49
|
+
};
|
|
50
|
+
const CustomIcon = !isVariantIcon(titleIconVariant) && titleIconVariant;
|
|
51
|
+
|
|
52
|
+
useIsomorphicLayoutEffect(() => {
|
|
53
|
+
setHasTooltip(h1.current && h1.current.offsetWidth < h1.current.scrollWidth);
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const content = (
|
|
57
|
+
<h1
|
|
58
|
+
id={id}
|
|
59
|
+
ref={h1}
|
|
60
|
+
className={css(modalStyles.modalBoxTitle, titleIconVariant && modalStyles.modifiers.icon, className)}
|
|
61
|
+
{...props}
|
|
62
|
+
>
|
|
63
|
+
{titleIconVariant && (
|
|
64
|
+
<span className={css(modalStyles.modalBoxTitleIcon)}>
|
|
65
|
+
{isVariantIcon(titleIconVariant) ? variantIcons[titleIconVariant] : <CustomIcon />}
|
|
66
|
+
</span>
|
|
67
|
+
)}
|
|
68
|
+
{label && <span className="pf-v5-screen-reader">{label}</span>}
|
|
69
|
+
<span className={css(modalStyles.modalBoxTitleText)}>{title}</span>
|
|
70
|
+
</h1>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
return hasTooltip ? <Tooltip content={title}>{content}</Tooltip> : content;
|
|
74
|
+
};
|
|
75
|
+
ModalBoxTitle.displayName = 'ModalBoxTitle';
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { FocusTrap } from '../../../helpers';
|
|
3
|
+
import bullsEyeStyles from '@patternfly/react-styles/css/layouts/Bullseye/bullseye';
|
|
4
|
+
import { css } from '@patternfly/react-styles';
|
|
5
|
+
import { getOUIAProps, OUIAProps } from '../../../helpers';
|
|
6
|
+
import { Backdrop } from '../../../components/Backdrop';
|
|
7
|
+
import { ModalBoxCloseButton } from './ModalBoxCloseButton';
|
|
8
|
+
import { ModalBox } from './ModalBox';
|
|
9
|
+
|
|
10
|
+
export interface ModalContentProps extends OUIAProps {
|
|
11
|
+
/** Id to use for the modal box description. This should match the ModalHeader labelId or descriptorId. */
|
|
12
|
+
'aria-describedby'?: string;
|
|
13
|
+
/** Accessible descriptor of the modal. */
|
|
14
|
+
'aria-label'?: string;
|
|
15
|
+
/** Id to use for the modal box label. This should include the ModalHeader labelId. */
|
|
16
|
+
'aria-labelledby'?: string;
|
|
17
|
+
/** Id of the modal box container. */
|
|
18
|
+
boxId: string;
|
|
19
|
+
/** Content rendered inside the modal. */
|
|
20
|
+
children: React.ReactNode;
|
|
21
|
+
/** Additional classes added to the modal box. */
|
|
22
|
+
className?: string;
|
|
23
|
+
/** Flag to disable focus trap. */
|
|
24
|
+
disableFocusTrap?: boolean;
|
|
25
|
+
/** The element to focus when the modal opens. By default the first
|
|
26
|
+
* focusable element will receive focus.
|
|
27
|
+
*/
|
|
28
|
+
elementToFocus?: HTMLElement | SVGElement | string;
|
|
29
|
+
/** Flag to show the modal. */
|
|
30
|
+
isOpen?: boolean;
|
|
31
|
+
/** A callback for when the close button is clicked. */
|
|
32
|
+
onClose?: (event: KeyboardEvent | React.MouseEvent) => void;
|
|
33
|
+
/** Position of the modal. By default a modal will be positioned vertically and horizontally centered. */
|
|
34
|
+
position?: 'default' | 'top';
|
|
35
|
+
/** Offset from alternate position. Can be any valid CSS length/percentage. */
|
|
36
|
+
positionOffset?: string;
|
|
37
|
+
/** Variant of the modal. */
|
|
38
|
+
variant?: 'small' | 'medium' | 'large' | 'default';
|
|
39
|
+
/** Default width of the modal. */
|
|
40
|
+
width?: number | string;
|
|
41
|
+
/** Maximum width of the modal. */
|
|
42
|
+
maxWidth?: number | string;
|
|
43
|
+
/** Value to overwrite the randomly generated data-ouia-component-id.*/
|
|
44
|
+
ouiaId?: number | string;
|
|
45
|
+
/** Set the value of data-ouia-safe. Only set to true when the component is in a static state, i.e. no animations are occurring. At all other times, this value must be false. */
|
|
46
|
+
ouiaSafe?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
50
|
+
children,
|
|
51
|
+
className,
|
|
52
|
+
isOpen = false,
|
|
53
|
+
'aria-label': ariaLabel,
|
|
54
|
+
'aria-describedby': ariaDescribedby,
|
|
55
|
+
'aria-labelledby': ariaLabelledby,
|
|
56
|
+
onClose,
|
|
57
|
+
variant = 'default',
|
|
58
|
+
position,
|
|
59
|
+
positionOffset,
|
|
60
|
+
width,
|
|
61
|
+
maxWidth,
|
|
62
|
+
boxId,
|
|
63
|
+
disableFocusTrap = false,
|
|
64
|
+
ouiaId,
|
|
65
|
+
ouiaSafe = true,
|
|
66
|
+
elementToFocus,
|
|
67
|
+
...props
|
|
68
|
+
}: ModalContentProps) => {
|
|
69
|
+
if (!isOpen) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const ariaLabelledbyFormatted = (): string => {
|
|
74
|
+
const idRefList: string[] = [];
|
|
75
|
+
if (ariaLabel && boxId) {
|
|
76
|
+
idRefList.push(ariaLabel && boxId);
|
|
77
|
+
}
|
|
78
|
+
if (ariaLabelledby) {
|
|
79
|
+
idRefList.push(ariaLabelledby);
|
|
80
|
+
}
|
|
81
|
+
if (idRefList.length === 0) {
|
|
82
|
+
return undefined;
|
|
83
|
+
} else {
|
|
84
|
+
return idRefList.join(' ');
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const modalBox = (
|
|
89
|
+
<ModalBox
|
|
90
|
+
className={css(className)}
|
|
91
|
+
variant={variant}
|
|
92
|
+
position={position}
|
|
93
|
+
positionOffset={positionOffset}
|
|
94
|
+
aria-label={ariaLabel}
|
|
95
|
+
aria-labelledby={ariaLabelledbyFormatted()}
|
|
96
|
+
aria-describedby={ariaDescribedby}
|
|
97
|
+
{...getOUIAProps(ModalContent.displayName, ouiaId, ouiaSafe)}
|
|
98
|
+
style={
|
|
99
|
+
{
|
|
100
|
+
...(width && { '--pf-v5-c-modal-box--Width': typeof width !== 'number' ? width : `${width}px` }),
|
|
101
|
+
...(maxWidth && {
|
|
102
|
+
'--pf-v5-c-modal-box--MaxWidth': typeof maxWidth !== 'number' ? maxWidth : `${maxWidth}px`
|
|
103
|
+
})
|
|
104
|
+
} as React.CSSProperties
|
|
105
|
+
}
|
|
106
|
+
{...props}
|
|
107
|
+
id={boxId}
|
|
108
|
+
>
|
|
109
|
+
{onClose && <ModalBoxCloseButton onClose={(event) => onClose(event)} ouiaId={ouiaId} />}
|
|
110
|
+
{children}
|
|
111
|
+
</ModalBox>
|
|
112
|
+
);
|
|
113
|
+
return (
|
|
114
|
+
<Backdrop>
|
|
115
|
+
<FocusTrap
|
|
116
|
+
active={!disableFocusTrap}
|
|
117
|
+
focusTrapOptions={{
|
|
118
|
+
clickOutsideDeactivates: true,
|
|
119
|
+
tabbableOptions: { displayCheck: 'none' },
|
|
120
|
+
// FocusTrap's initialFocus can accept false as a value to prevent initial focus.
|
|
121
|
+
// We want to prevent this in case false is ever passed in.
|
|
122
|
+
initialFocus: elementToFocus || undefined
|
|
123
|
+
}}
|
|
124
|
+
className={css(bullsEyeStyles.bullseye)}
|
|
125
|
+
>
|
|
126
|
+
{modalBox}
|
|
127
|
+
</FocusTrap>
|
|
128
|
+
</Backdrop>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
ModalContent.displayName = 'ModalContent';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { css } from '@patternfly/react-styles';
|
|
3
|
+
import styles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
|
|
4
|
+
|
|
5
|
+
/** Renders content in the footer of the modal */
|
|
6
|
+
|
|
7
|
+
export interface ModalFooterProps {
|
|
8
|
+
/** Content rendered inside the modal footer. */
|
|
9
|
+
children?: React.ReactNode;
|
|
10
|
+
/** Additional classes added to the modal footer. */
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ModalFooter: React.FunctionComponent<ModalFooterProps> = ({
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: ModalFooterProps) => (
|
|
19
|
+
<footer {...props} className={css(styles.modalBoxFooter, className)}>
|
|
20
|
+
{children}
|
|
21
|
+
</footer>
|
|
22
|
+
);
|
|
23
|
+
ModalFooter.displayName = 'ModalFooter';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { css } from '@patternfly/react-styles';
|
|
3
|
+
import styles from '@patternfly/react-styles/css/components/ModalBox/modal-box';
|
|
4
|
+
import { ModalBoxDescription } from './ModalBoxDescription';
|
|
5
|
+
import { ModalBoxTitle } from './ModalBoxTitle';
|
|
6
|
+
|
|
7
|
+
/** Renders content in the header of the modal */
|
|
8
|
+
|
|
9
|
+
export interface ModalHeaderProps {
|
|
10
|
+
/** Custom content rendered inside the modal header. If children are supplied then the tile, tileIconVariant and titleScreenReaderText props are ignored. */
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
/** Additional classes added to the modal header. */
|
|
13
|
+
className?: string;
|
|
14
|
+
/** Description of the modal. */
|
|
15
|
+
description?: React.ReactNode;
|
|
16
|
+
/** Id of the modal description. */
|
|
17
|
+
descriptorId?: string;
|
|
18
|
+
/** Optional help section for the modal header. */
|
|
19
|
+
help?: React.ReactNode;
|
|
20
|
+
/** Id of the modal title. */
|
|
21
|
+
labelId?: string;
|
|
22
|
+
/** Content rendered inside the modal title. */
|
|
23
|
+
title?: React.ReactNode;
|
|
24
|
+
/** Optional alert icon (or other) to show before the title. When the predefined alert types
|
|
25
|
+
* are used the default styling will be automatically applied. */
|
|
26
|
+
titleIconVariant?: 'success' | 'danger' | 'warning' | 'info' | 'custom' | React.ComponentType<any>;
|
|
27
|
+
/** Optional title label text for screen readers. */
|
|
28
|
+
titleScreenReaderText?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const ModalHeader: React.FunctionComponent<ModalHeaderProps> = ({
|
|
32
|
+
children,
|
|
33
|
+
className,
|
|
34
|
+
descriptorId,
|
|
35
|
+
description,
|
|
36
|
+
labelId,
|
|
37
|
+
title,
|
|
38
|
+
titleIconVariant,
|
|
39
|
+
titleScreenReaderText,
|
|
40
|
+
help,
|
|
41
|
+
...props
|
|
42
|
+
}: ModalHeaderProps) => {
|
|
43
|
+
const headerContent = children ? (
|
|
44
|
+
children
|
|
45
|
+
) : (
|
|
46
|
+
<>
|
|
47
|
+
<ModalBoxTitle
|
|
48
|
+
title={title}
|
|
49
|
+
titleIconVariant={titleIconVariant}
|
|
50
|
+
titleScreenReaderText={titleScreenReaderText}
|
|
51
|
+
id={labelId}
|
|
52
|
+
/>
|
|
53
|
+
{description && <ModalBoxDescription id={descriptorId}>{description}</ModalBoxDescription>}
|
|
54
|
+
</>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// TODO: apply variant modifier for icon styling. Core fix needed first. similar to this:
|
|
58
|
+
// className={css(className, isVariantIcon(titleIconVariant) && modalStyles.modifiers[titleIconVariant])}
|
|
59
|
+
return (
|
|
60
|
+
<header className={css(styles.modalBoxHeader, help && styles.modifiers.help, className)} {...props}>
|
|
61
|
+
{help && (
|
|
62
|
+
<>
|
|
63
|
+
<div className={css(styles.modalBoxHeaderMain)}>{headerContent}</div>
|
|
64
|
+
<div className={`${styles.modalBoxHeader}-help`}>{help}</div>
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
67
|
+
{!help && headerContent}
|
|
68
|
+
</header>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
ModalHeader.displayName = 'ModalHeader';
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
|
|
6
|
+
import { css } from '../../../../../../react-styles/dist/js';
|
|
7
|
+
import styles from '@patternfly/react-styles/css/components/Backdrop/backdrop';
|
|
8
|
+
|
|
9
|
+
import { Modal } from '../Modal';
|
|
10
|
+
import { KeyTypes } from '../../../../helpers';
|
|
11
|
+
|
|
12
|
+
jest.spyOn(document, 'createElement');
|
|
13
|
+
jest.spyOn(document.body, 'addEventListener');
|
|
14
|
+
|
|
15
|
+
const props = {
|
|
16
|
+
onClose: jest.fn(),
|
|
17
|
+
isOpen: false,
|
|
18
|
+
children: 'modal content'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const target = document.createElement('div');
|
|
22
|
+
|
|
23
|
+
const ModalWithSiblings = () => {
|
|
24
|
+
const [isOpen, setIsOpen] = React.useState(true);
|
|
25
|
+
const [isModalMounted, setIsModalMounted] = React.useState(true);
|
|
26
|
+
const modalProps = { ...props, isOpen, appendTo: target, onClose: () => setIsOpen(false) };
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
<aside>Aside sibling</aside>
|
|
31
|
+
<article>Section sibling</article>
|
|
32
|
+
{isModalMounted && (
|
|
33
|
+
<Modal {...modalProps}>
|
|
34
|
+
<button onClick={() => setIsModalMounted(false)}>Unmount Modal</button>
|
|
35
|
+
</Modal>
|
|
36
|
+
)}
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe('Modal', () => {
|
|
42
|
+
test('Modal creates a container element once for div', () => {
|
|
43
|
+
render(<Modal {...props} />);
|
|
44
|
+
expect(document.createElement).toHaveBeenCalledWith('div');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('modal closes with escape', async () => {
|
|
48
|
+
const user = userEvent.setup();
|
|
49
|
+
|
|
50
|
+
render(<Modal {...props} isOpen appendTo={document.body} aria-label="modal-div" />);
|
|
51
|
+
|
|
52
|
+
await user.type(screen.getByLabelText('modal-div'), `{${KeyTypes.Escape}}`);
|
|
53
|
+
expect(props.onClose).toHaveBeenCalled();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('modal does not call onClose for esc key if it is not open', () => {
|
|
57
|
+
render(<Modal {...props} />);
|
|
58
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
59
|
+
expect(props.onClose).not.toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('modal has body backdropOpen class when open', () => {
|
|
63
|
+
render(<Modal {...props} isOpen />);
|
|
64
|
+
expect(document.body).toHaveClass(css(styles.backdropOpen));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('modal has no body backdropOpen class when not open', () => {
|
|
68
|
+
render(<Modal {...props} />);
|
|
69
|
+
expect(document.body).not.toHaveClass(css(styles.backdropOpen));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('modal shows the close button when onClose prop is passed (true by default)', () => {
|
|
73
|
+
render(<Modal {...props} isOpen />);
|
|
74
|
+
expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('modal does not show the close button when onClose not passed', () => {
|
|
78
|
+
render(
|
|
79
|
+
<Modal isOpen disableFocusTrap>
|
|
80
|
+
No close button{' '}
|
|
81
|
+
</Modal>
|
|
82
|
+
);
|
|
83
|
+
expect(screen.queryByRole('button', { name: 'Close' })).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('modal generates console error when no accessible name is provided', () => {
|
|
87
|
+
const props = {
|
|
88
|
+
onClose: jest.fn(),
|
|
89
|
+
isOpen: true,
|
|
90
|
+
children: 'modal content'
|
|
91
|
+
};
|
|
92
|
+
const consoleErrorMock = jest.fn();
|
|
93
|
+
global.console = { error: consoleErrorMock } as any;
|
|
94
|
+
|
|
95
|
+
render(<Modal {...props} />);
|
|
96
|
+
|
|
97
|
+
expect(consoleErrorMock).toHaveBeenCalled();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('modal adds aria-hidden attribute to its siblings when open', () => {
|
|
101
|
+
render(<ModalWithSiblings />, { container: document.body.appendChild(target) });
|
|
102
|
+
|
|
103
|
+
const asideSibling = screen.getByRole('complementary', { hidden: true });
|
|
104
|
+
const articleSibling = screen.getByRole('article', { hidden: true });
|
|
105
|
+
|
|
106
|
+
expect(asideSibling).toHaveAttribute('aria-hidden');
|
|
107
|
+
expect(articleSibling).toHaveAttribute('aria-hidden');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('modal removes the aria-hidden attribute from its siblings when closed', async () => {
|
|
111
|
+
const user = userEvent.setup();
|
|
112
|
+
|
|
113
|
+
render(<ModalWithSiblings />, { container: document.body.appendChild(target) });
|
|
114
|
+
|
|
115
|
+
const asideSibling = screen.getByRole('complementary', { hidden: true });
|
|
116
|
+
const articleSibling = screen.getByRole('article', { hidden: true });
|
|
117
|
+
const closeButton = screen.getByRole('button', { name: 'Close' });
|
|
118
|
+
|
|
119
|
+
expect(articleSibling).toHaveAttribute('aria-hidden');
|
|
120
|
+
expect(asideSibling).toHaveAttribute('aria-hidden');
|
|
121
|
+
|
|
122
|
+
await user.click(closeButton);
|
|
123
|
+
|
|
124
|
+
expect(articleSibling).not.toHaveAttribute('aria-hidden');
|
|
125
|
+
expect(asideSibling).not.toHaveAttribute('aria-hidden');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('modal removes the aria-hidden attribute from its siblings when unmounted', async () => {
|
|
129
|
+
const user = userEvent.setup();
|
|
130
|
+
|
|
131
|
+
render(<ModalWithSiblings />, { container: document.body.appendChild(target) });
|
|
132
|
+
|
|
133
|
+
const asideSibling = screen.getByRole('complementary', { hidden: true });
|
|
134
|
+
const articleSibling = screen.getByRole('article', { hidden: true });
|
|
135
|
+
const unmountButton = screen.getByRole('button', { name: 'Unmount Modal' });
|
|
136
|
+
|
|
137
|
+
expect(asideSibling).toHaveAttribute('aria-hidden');
|
|
138
|
+
expect(articleSibling).toHaveAttribute('aria-hidden');
|
|
139
|
+
|
|
140
|
+
await user.click(unmountButton);
|
|
141
|
+
|
|
142
|
+
expect(asideSibling).not.toHaveAttribute('aria-hidden');
|
|
143
|
+
expect(articleSibling).not.toHaveAttribute('aria-hidden');
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
|
|
4
|
+
import { ModalBody } from '../ModalBody';
|
|
5
|
+
|
|
6
|
+
describe('ModalBody tests', () => {
|
|
7
|
+
test('ModalBody renders', () => {
|
|
8
|
+
const { asFragment } = render(
|
|
9
|
+
<ModalBody id="id" className="test-box-class">
|
|
10
|
+
This is a Modal body
|
|
11
|
+
</ModalBody>
|
|
12
|
+
);
|
|
13
|
+
expect(asFragment()).toMatchSnapshot();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('The ModalBody has the expected aria-label when it is passed', () => {
|
|
17
|
+
const props = {
|
|
18
|
+
isOpen: true
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
render(
|
|
22
|
+
<ModalBody aria-label="modal body aria label" {...props}>
|
|
23
|
+
This is a ModalBox
|
|
24
|
+
</ModalBody>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const modalBoxBody = screen.getByText('This is a ModalBox');
|
|
28
|
+
expect(modalBoxBody).toHaveAccessibleName('modal body aria label');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('The modalBoxBody has the expected aria role when aria-label is passed and role is not', () => {
|
|
32
|
+
const props = {
|
|
33
|
+
isOpen: true
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
render(
|
|
37
|
+
<ModalBody aria-label="modal body aria label" {...props}>
|
|
38
|
+
This is a ModalBox
|
|
39
|
+
</ModalBody>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const modalBoxBody = screen.getByRole('region', { name: 'modal body aria label' });
|
|
43
|
+
expect(modalBoxBody).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('The modalBoxBody has the expected aria role when bodyAriaRole is passed', () => {
|
|
47
|
+
const props = {
|
|
48
|
+
isOpen: true
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
render(
|
|
52
|
+
<ModalBody aria-label="modal body aria label" role="article" {...props}>
|
|
53
|
+
This is a ModalBox
|
|
54
|
+
</ModalBody>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const modalBoxBody = screen.getByRole('article', { name: 'modal body aria label' });
|
|
58
|
+
expect(modalBoxBody).toBeInTheDocument();
|
|
59
|
+
});
|
|
60
|
+
});
|