@patternfly/react-core 6.7.0-prerelease.1 → 6.7.0-prerelease.2
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/ClipboardCopy/package.json +1 -1
- package/dist/dynamic/components/CodeBlock/package.json +1 -1
- package/dist/dynamic/components/Compass/package.json +1 -1
- package/dist/dynamic/components/Content/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/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/Hero/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/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/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/Chip/package.json +1 -1
- package/dist/dynamic/deprecated/components/DragDrop/package.json +1 -1
- package/dist/dynamic/deprecated/components/DualListSelector/package.json +1 -1
- package/dist/dynamic/deprecated/components/Modal/package.json +1 -1
- package/dist/dynamic/deprecated/components/Tile/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/AnimationsProvider/AnimationsProvider/package.json +1 -1
- package/dist/dynamic/helpers/AnimationsProvider/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/SSRSafeIds/SSRSafeIds/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/fileUtils/package.json +1 -1
- package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
- package/dist/dynamic/helpers/inert/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/useSSRSafeId/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/styles/package.json +1 -1
- package/dist/esm/components/Backdrop/Backdrop.d.ts +4 -0
- package/dist/esm/components/Backdrop/Backdrop.d.ts.map +1 -1
- package/dist/esm/components/Backdrop/Backdrop.js +4 -2
- package/dist/esm/components/Backdrop/Backdrop.js.map +1 -1
- package/dist/esm/components/Modal/Modal.d.ts +2 -0
- package/dist/esm/components/Modal/Modal.d.ts.map +1 -1
- package/dist/esm/components/Modal/Modal.js +2 -1
- package/dist/esm/components/Modal/Modal.js.map +1 -1
- package/dist/esm/components/Modal/ModalBox.d.ts +4 -0
- package/dist/esm/components/Modal/ModalBox.d.ts.map +1 -1
- package/dist/esm/components/Modal/ModalBox.js +2 -2
- package/dist/esm/components/Modal/ModalBox.js.map +1 -1
- package/dist/esm/components/Modal/ModalContent.d.ts +2 -0
- package/dist/esm/components/Modal/ModalContent.d.ts.map +1 -1
- package/dist/esm/components/Modal/ModalContent.js +40 -5
- package/dist/esm/components/Modal/ModalContent.js.map +1 -1
- package/dist/js/components/Backdrop/Backdrop.d.ts +4 -0
- package/dist/js/components/Backdrop/Backdrop.d.ts.map +1 -1
- package/dist/js/components/Backdrop/Backdrop.js +4 -2
- package/dist/js/components/Backdrop/Backdrop.js.map +1 -1
- package/dist/js/components/Modal/Modal.d.ts +2 -0
- package/dist/js/components/Modal/Modal.d.ts.map +1 -1
- package/dist/js/components/Modal/Modal.js +2 -1
- package/dist/js/components/Modal/Modal.js.map +1 -1
- package/dist/js/components/Modal/ModalBox.d.ts +4 -0
- package/dist/js/components/Modal/ModalBox.d.ts.map +1 -1
- package/dist/js/components/Modal/ModalBox.js +2 -2
- package/dist/js/components/Modal/ModalBox.js.map +1 -1
- package/dist/js/components/Modal/ModalContent.d.ts +2 -0
- package/dist/js/components/Modal/ModalContent.d.ts.map +1 -1
- package/dist/js/components/Modal/ModalContent.js +39 -4
- package/dist/js/components/Modal/ModalContent.js.map +1 -1
- package/dist/umd/assets/{output-Doo5KfbL.css → output-BPcxNyfB.css} +15654 -15654
- package/dist/umd/react-core.min.js +1 -1
- package/helpers/package.json +1 -1
- package/layouts/package.json +1 -1
- package/next/package.json +1 -1
- package/package.json +3 -3
- package/src/components/Backdrop/Backdrop.tsx +24 -5
- package/src/components/Backdrop/__tests__/Backdrop.test.tsx +35 -1
- package/src/components/Backdrop/__tests__/__snapshots__/Backdrop.test.tsx.snap +1 -1
- package/src/components/Modal/Modal.tsx +4 -1
- package/src/components/Modal/ModalBox.tsx +8 -0
- package/src/components/Modal/ModalContent.tsx +54 -4
- package/src/components/Modal/__tests__/Modal.test.tsx +14 -0
- package/src/components/Modal/__tests__/ModalContent.test.tsx +75 -1
- package/src/components/Modal/examples/Modal.md +12 -0
- package/src/components/Modal/examples/ModalNotAnimated.tsx +44 -0
- package/src/deprecated/components/Modal/__tests__/__snapshots__/ModalContent.test.tsx.snap +8 -8
package/helpers/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@patternfly/react-core-helpers","main":"../dist/js/helpers/index.js","module":"../dist/esm/helpers/index.js","typings":"../dist/esm/helpers/index.d.ts","version":"6.7.0-prerelease.
|
|
1
|
+
{"name":"@patternfly/react-core-helpers","main":"../dist/js/helpers/index.js","module":"../dist/esm/helpers/index.js","typings":"../dist/esm/helpers/index.d.ts","version":"6.7.0-prerelease.1","private":true}
|
package/layouts/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@patternfly/react-core-layouts","main":"../dist/js/layouts/index.js","module":"../dist/esm/layouts/index.js","typings":"../dist/esm/layouts/index.d.ts","version":"6.7.0-prerelease.
|
|
1
|
+
{"name":"@patternfly/react-core-layouts","main":"../dist/js/layouts/index.js","module":"../dist/esm/layouts/index.js","typings":"../dist/esm/layouts/index.d.ts","version":"6.7.0-prerelease.1","private":true}
|
package/next/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@patternfly/react-core-next","main":"../dist/js/next/index.js","module":"../dist/esm/next/index.js","typings":"../dist/esm/next/index.d.ts","version":"6.7.0-prerelease.
|
|
1
|
+
{"name":"@patternfly/react-core-next","main":"../dist/js/next/index.js","module":"../dist/esm/next/index.js","typings":"../dist/esm/next/index.d.ts","version":"6.7.0-prerelease.1","private":true}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "6.7.0-prerelease.
|
|
3
|
+
"version": "6.7.0-prerelease.2",
|
|
4
4
|
"description": "This library provides a set of common React components for use with the PatternFly reference implementation.",
|
|
5
5
|
"main": "dist/js/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@patternfly/react-icons": "^6.7.0-prerelease.0",
|
|
50
50
|
"@patternfly/react-styles": "^6.7.0-prerelease.0",
|
|
51
|
-
"@patternfly/react-tokens": "^6.7.0-prerelease.
|
|
51
|
+
"@patternfly/react-tokens": "^6.7.0-prerelease.1",
|
|
52
52
|
"focus-trap": "7.6.6",
|
|
53
53
|
"react-dropzone": "^14.3.5",
|
|
54
54
|
"tslib": "^2.8.1"
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"react": "^17 || ^18 || ^19",
|
|
64
64
|
"react-dom": "^17 || ^18 || ^19"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "6a486021829bef4ba8c6a772c035b364ab5f0686"
|
|
67
67
|
}
|
|
@@ -1,20 +1,39 @@
|
|
|
1
1
|
import { css } from '@patternfly/react-styles';
|
|
2
2
|
import styles from '@patternfly/react-styles/css/components/Backdrop/backdrop';
|
|
3
|
+
import { useHasAnimations } from '../../helpers';
|
|
3
4
|
|
|
4
5
|
export interface BackdropProps extends React.HTMLProps<HTMLDivElement> {
|
|
5
6
|
/** Content rendered inside the backdrop */
|
|
6
7
|
children?: React.ReactNode;
|
|
7
8
|
/** Additional classes added to the backdrop */
|
|
8
9
|
className?: string;
|
|
10
|
+
/** Flag indicating whether animations are enabled. Animations are enabled by default. */
|
|
11
|
+
hasAnimations?: boolean;
|
|
12
|
+
/** Flag to show the backdrop when animations are enabled. Set to false while the backdrop remains mounted to play its exit transition. */
|
|
13
|
+
isVisible?: boolean;
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
export const Backdrop: React.FunctionComponent<BackdropProps> = ({
|
|
12
17
|
children = null,
|
|
13
18
|
className = '',
|
|
19
|
+
hasAnimations: hasAnimationsProp = true,
|
|
20
|
+
isVisible = true,
|
|
14
21
|
...props
|
|
15
|
-
}: BackdropProps) =>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
}: BackdropProps) => {
|
|
23
|
+
const hasAnimations = useHasAnimations(hasAnimationsProp);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div
|
|
27
|
+
{...props}
|
|
28
|
+
className={css(
|
|
29
|
+
styles.backdrop,
|
|
30
|
+
hasAnimations && styles.modifiers.animate,
|
|
31
|
+
hasAnimations && isVisible && styles.modifiers.show,
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
20
39
|
Backdrop.displayName = 'Backdrop';
|
|
@@ -21,11 +21,45 @@ test(`Renders with the ${styles.backdrop}`, () => {
|
|
|
21
21
|
expect(screen.getByText('Test')).toHaveClass(styles.backdrop);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
test(
|
|
24
|
+
test('Renders with animations enabled by default', () => {
|
|
25
25
|
render(<Backdrop>Test</Backdrop>);
|
|
26
|
+
expect(screen.getByText('Test')).toHaveClass(styles.backdrop, styles.modifiers.animate, styles.modifiers.show, {
|
|
27
|
+
exact: true
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('Renders without animation classes when animations are explicitly disabled', () => {
|
|
32
|
+
render(<Backdrop hasAnimations={false}>Test</Backdrop>);
|
|
26
33
|
expect(screen.getByText('Test')).toHaveClass(styles.backdrop, { exact: true });
|
|
27
34
|
});
|
|
28
35
|
|
|
36
|
+
test('Renders as visible by default when animations are enabled', () => {
|
|
37
|
+
render(<Backdrop hasAnimations>Test</Backdrop>);
|
|
38
|
+
expect(screen.getByText('Test')).toHaveClass(styles.backdrop, styles.modifiers.animate, styles.modifiers.show, {
|
|
39
|
+
exact: true
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Renders as hidden when animations are enabled and isVisible is false', () => {
|
|
44
|
+
render(
|
|
45
|
+
<Backdrop hasAnimations isVisible={false}>
|
|
46
|
+
Test
|
|
47
|
+
</Backdrop>
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByText('Test')).toHaveClass(styles.backdrop, styles.modifiers.animate, { exact: true });
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('Renders as visible when animations are enabled and isVisible is true', () => {
|
|
53
|
+
render(
|
|
54
|
+
<Backdrop hasAnimations isVisible>
|
|
55
|
+
Test
|
|
56
|
+
</Backdrop>
|
|
57
|
+
);
|
|
58
|
+
expect(screen.getByText('Test')).toHaveClass(styles.backdrop, styles.modifiers.animate, styles.modifiers.show, {
|
|
59
|
+
exact: true
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
29
63
|
test('Renders with custom class name when className prop is passed', () => {
|
|
30
64
|
render(<Backdrop className="test-class">Test</Backdrop>);
|
|
31
65
|
expect(screen.getByText('Test')).toHaveClass('test-class');
|
|
@@ -53,6 +53,8 @@ export interface ModalProps extends React.HTMLProps<HTMLDivElement>, OUIAProps {
|
|
|
53
53
|
ouiaId?: number | string;
|
|
54
54
|
/** 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. */
|
|
55
55
|
ouiaSafe?: boolean;
|
|
56
|
+
/** Flag indicating whether animations are enabled. Animations are enabled by default. */
|
|
57
|
+
hasAnimations?: boolean;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export enum ModalVariant {
|
|
@@ -78,7 +80,8 @@ class Modal extends Component<ModalProps, ModalState> {
|
|
|
78
80
|
variant: 'default',
|
|
79
81
|
appendTo: () => document.body,
|
|
80
82
|
ouiaSafe: true,
|
|
81
|
-
position: 'default'
|
|
83
|
+
position: 'default',
|
|
84
|
+
hasAnimations: true
|
|
82
85
|
};
|
|
83
86
|
|
|
84
87
|
constructor(props: ModalProps) {
|
|
@@ -19,6 +19,10 @@ export interface ModalBoxProps extends React.HTMLProps<HTMLDivElement> {
|
|
|
19
19
|
positionOffset?: string;
|
|
20
20
|
/** Variant of the modal. */
|
|
21
21
|
variant?: 'small' | 'medium' | 'large' | 'default';
|
|
22
|
+
/** Flag indicating whether animations are enabled. */
|
|
23
|
+
hasAnimations?: boolean;
|
|
24
|
+
/** Flag to show the modal. */
|
|
25
|
+
isOpen?: boolean;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
|
|
@@ -31,6 +35,8 @@ export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
|
|
|
31
35
|
'aria-label': ariaLabel,
|
|
32
36
|
'aria-describedby': ariaDescribedby,
|
|
33
37
|
style,
|
|
38
|
+
isOpen,
|
|
39
|
+
hasAnimations,
|
|
34
40
|
...props
|
|
35
41
|
}: ModalBoxProps) => {
|
|
36
42
|
if (positionOffset) {
|
|
@@ -46,6 +52,8 @@ export const ModalBox: React.FunctionComponent<ModalBoxProps> = ({
|
|
|
46
52
|
aria-modal="true"
|
|
47
53
|
className={css(
|
|
48
54
|
styles.modalBox,
|
|
55
|
+
hasAnimations && styles.modifiers.animate,
|
|
56
|
+
hasAnimations && isOpen === true && styles.modifiers.open,
|
|
49
57
|
className,
|
|
50
58
|
position === 'top' && styles.modifiers.alignTop,
|
|
51
59
|
variant === 'large' && styles.modifiers.lg,
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
1
2
|
import { FocusTrap } from '../../helpers';
|
|
2
3
|
import bullsEyeStyles from '@patternfly/react-styles/css/layouts/Bullseye/bullseye';
|
|
3
4
|
import { css } from '@patternfly/react-styles';
|
|
4
|
-
import { getOUIAProps, OUIAProps } from '../../helpers';
|
|
5
|
+
import { getOUIAProps, OUIAProps, useHasAnimations } from '../../helpers';
|
|
5
6
|
import { Backdrop } from '../Backdrop';
|
|
6
7
|
import { ModalBoxCloseButton } from './ModalBoxCloseButton';
|
|
7
8
|
import { ModalBox } from './ModalBox';
|
|
8
9
|
|
|
10
|
+
const transitionEndFallbackDelay = 300;
|
|
11
|
+
|
|
9
12
|
export interface ModalContentProps extends OUIAProps {
|
|
10
13
|
/** Id to use for the modal box description. This should match the ModalHeader labelId or descriptorId. */
|
|
11
14
|
'aria-describedby'?: string;
|
|
@@ -49,6 +52,8 @@ export interface ModalContentProps extends OUIAProps {
|
|
|
49
52
|
ouiaId?: number | string;
|
|
50
53
|
/** 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. */
|
|
51
54
|
ouiaSafe?: boolean;
|
|
55
|
+
/** Flag indicating whether animations are enabled. */
|
|
56
|
+
hasAnimations?: boolean;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
@@ -72,9 +77,30 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
|
72
77
|
ouiaSafe = true,
|
|
73
78
|
elementToFocus,
|
|
74
79
|
focusTrapId,
|
|
80
|
+
hasAnimations: hasAnimationsProp,
|
|
75
81
|
...props
|
|
76
82
|
}: ModalContentProps) => {
|
|
77
|
-
|
|
83
|
+
const hasAnimations = useHasAnimations(hasAnimationsProp);
|
|
84
|
+
// Keeps the modal in the DOM while the close animation runs. When animations are enabled we defer
|
|
85
|
+
// unmounting until the backdrop's transition ends (see onTransitionEnd below) instead of removing
|
|
86
|
+
// it immediately when isOpen becomes false.
|
|
87
|
+
const [isRendered, setIsRendered] = useState(isOpen);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (isOpen) {
|
|
91
|
+
setIsRendered(true);
|
|
92
|
+
} else if (!isRendered) {
|
|
93
|
+
return;
|
|
94
|
+
} else if (!hasAnimations) {
|
|
95
|
+
setIsRendered(false);
|
|
96
|
+
} else {
|
|
97
|
+
// Ensure the modal is removed if CSS transitions are disabled or transitionend does not fire.
|
|
98
|
+
const transitionEndFallback = window.setTimeout(() => setIsRendered(false), transitionEndFallbackDelay);
|
|
99
|
+
return () => window.clearTimeout(transitionEndFallback);
|
|
100
|
+
}
|
|
101
|
+
}, [isOpen, hasAnimations, isRendered]);
|
|
102
|
+
|
|
103
|
+
if (!isRendered) {
|
|
78
104
|
return null;
|
|
79
105
|
}
|
|
80
106
|
|
|
@@ -91,6 +117,8 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
|
91
117
|
const modalBox = (
|
|
92
118
|
<ModalBox
|
|
93
119
|
className={css(className)}
|
|
120
|
+
isOpen={isOpen}
|
|
121
|
+
hasAnimations={hasAnimations}
|
|
94
122
|
variant={variant}
|
|
95
123
|
position={position}
|
|
96
124
|
positionOffset={positionOffset}
|
|
@@ -113,10 +141,32 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
|
|
|
113
141
|
{children}
|
|
114
142
|
</ModalBox>
|
|
115
143
|
);
|
|
144
|
+
let focusTrapActive = !disableFocusTrap;
|
|
145
|
+
if (hasAnimations) {
|
|
146
|
+
focusTrapActive = !disableFocusTrap && isOpen;
|
|
147
|
+
}
|
|
148
|
+
|
|
116
149
|
return (
|
|
117
|
-
<Backdrop
|
|
150
|
+
<Backdrop
|
|
151
|
+
className={css(backdropClassName)}
|
|
152
|
+
id={backdropId}
|
|
153
|
+
hasAnimations={hasAnimations}
|
|
154
|
+
isVisible={isOpen}
|
|
155
|
+
aria-hidden={hasAnimations && !isOpen ? true : undefined}
|
|
156
|
+
onTransitionEnd={
|
|
157
|
+
hasAnimations
|
|
158
|
+
? (event) => {
|
|
159
|
+
// Only unmount once the backdrop's own closing transition finishes. Guarding on the
|
|
160
|
+
// target prevents bubbled transitions from child elements from triggering this early.
|
|
161
|
+
if (!isOpen && event.target === event.currentTarget) {
|
|
162
|
+
setIsRendered(false);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
: undefined
|
|
166
|
+
}
|
|
167
|
+
>
|
|
118
168
|
<FocusTrap
|
|
119
|
-
active={
|
|
169
|
+
active={focusTrapActive}
|
|
120
170
|
focusTrapOptions={{
|
|
121
171
|
clickOutsideDeactivates: true,
|
|
122
172
|
tabbableOptions: { displayCheck: 'none' },
|
|
@@ -111,6 +111,20 @@ describe('Modal', () => {
|
|
|
111
111
|
expect(document.body).toHaveClass(css(styles.backdropOpen));
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
+
test('modal has animations enabled by default', () => {
|
|
115
|
+
render(<Modal {...props} isOpen />);
|
|
116
|
+
const backdrop = screen.getByText('modal content').closest(`.${styles.backdrop}`);
|
|
117
|
+
|
|
118
|
+
expect(backdrop).toHaveClass(styles.modifiers.animate, styles.modifiers.show);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('modal animations can be explicitly disabled', () => {
|
|
122
|
+
render(<Modal {...props} isOpen hasAnimations={false} />);
|
|
123
|
+
const backdrop = screen.getByText('modal content').closest(`.${styles.backdrop}`);
|
|
124
|
+
|
|
125
|
+
expect(backdrop).not.toHaveClass(styles.modifiers.animate, styles.modifiers.show);
|
|
126
|
+
});
|
|
127
|
+
|
|
114
128
|
test('modal has no body backdropOpen class when not open', () => {
|
|
115
129
|
render(<Modal {...props} />);
|
|
116
130
|
expect(document.body).not.toHaveClass(css(styles.backdropOpen));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
1
|
+
import { act, render, screen } from '@testing-library/react';
|
|
2
2
|
|
|
3
3
|
import { ModalContent } from '../ModalContent';
|
|
4
4
|
|
|
@@ -59,3 +59,77 @@ test('Modal content can add id to focus trap correctly for use with dropdowns',
|
|
|
59
59
|
'pf-v6-l-bullseye'
|
|
60
60
|
);
|
|
61
61
|
});
|
|
62
|
+
|
|
63
|
+
test('Modal content is hidden from assistive technologies during its closing animation', () => {
|
|
64
|
+
const { rerender } = render(
|
|
65
|
+
<ModalContent isOpen hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
66
|
+
This is a ModalBox header
|
|
67
|
+
</ModalContent>
|
|
68
|
+
);
|
|
69
|
+
const backdrop = document.getElementById('backdropId');
|
|
70
|
+
|
|
71
|
+
expect(backdrop).not.toHaveAttribute('aria-hidden');
|
|
72
|
+
|
|
73
|
+
rerender(
|
|
74
|
+
<ModalContent isOpen={false} hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
75
|
+
This is a ModalBox header
|
|
76
|
+
</ModalContent>
|
|
77
|
+
);
|
|
78
|
+
expect(backdrop).toHaveAttribute('aria-hidden', 'true');
|
|
79
|
+
|
|
80
|
+
rerender(
|
|
81
|
+
<ModalContent isOpen hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
82
|
+
This is a ModalBox header
|
|
83
|
+
</ModalContent>
|
|
84
|
+
);
|
|
85
|
+
expect(backdrop).not.toHaveAttribute('aria-hidden');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('Modal content unmounts if its closing transition does not end', () => {
|
|
89
|
+
jest.useFakeTimers();
|
|
90
|
+
try {
|
|
91
|
+
const { rerender } = render(
|
|
92
|
+
<ModalContent isOpen hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
93
|
+
This is a ModalBox header
|
|
94
|
+
</ModalContent>
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
rerender(
|
|
98
|
+
<ModalContent isOpen={false} hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
99
|
+
This is a ModalBox header
|
|
100
|
+
</ModalContent>
|
|
101
|
+
);
|
|
102
|
+
expect(document.getElementById('backdropId')).toBeInTheDocument();
|
|
103
|
+
|
|
104
|
+
act(() => jest.runOnlyPendingTimers());
|
|
105
|
+
expect(document.getElementById('backdropId')).not.toBeInTheDocument();
|
|
106
|
+
} finally {
|
|
107
|
+
jest.useRealTimers();
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Modal content remains mounted during close when reduced motion is preferred', () => {
|
|
112
|
+
const matchMedia = window.matchMedia;
|
|
113
|
+
window.matchMedia = jest.fn().mockReturnValue({ matches: true } as MediaQueryList);
|
|
114
|
+
jest.useFakeTimers();
|
|
115
|
+
try {
|
|
116
|
+
const { rerender } = render(
|
|
117
|
+
<ModalContent isOpen hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
118
|
+
This is a ModalBox header
|
|
119
|
+
</ModalContent>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
rerender(
|
|
123
|
+
<ModalContent isOpen={false} hasAnimations backdropId="backdropId" {...modalContentProps}>
|
|
124
|
+
This is a ModalBox header
|
|
125
|
+
</ModalContent>
|
|
126
|
+
);
|
|
127
|
+
expect(document.getElementById('backdropId')).toBeInTheDocument();
|
|
128
|
+
|
|
129
|
+
act(() => jest.runOnlyPendingTimers());
|
|
130
|
+
expect(document.getElementById('backdropId')).not.toBeInTheDocument();
|
|
131
|
+
} finally {
|
|
132
|
+
jest.useRealTimers();
|
|
133
|
+
window.matchMedia = matchMedia;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
@@ -12,6 +12,10 @@ import RhUiAttentionBellFillIcon from '@patternfly/react-icons/dist/esm/icons/rh
|
|
|
12
12
|
import RhUiQuestionMarkCircleIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-question-mark-circle-icon';
|
|
13
13
|
import formStyles from '@patternfly/react-styles/css/components/Form/form';
|
|
14
14
|
|
|
15
|
+
When using the Modal component, it is recommended to control it via the `isOpen` property rather than conditionally rendering the Modal.
|
|
16
|
+
|
|
17
|
+
Conditional rendering can potentially lead to unforeseen issues, especially with animations enabled.
|
|
18
|
+
|
|
15
19
|
## Examples
|
|
16
20
|
|
|
17
21
|
### Basic modals
|
|
@@ -154,3 +158,11 @@ To customize which element inside the modal receives focus when initially opened
|
|
|
154
158
|
```ts file="./ModalCustomFocus.tsx"
|
|
155
159
|
|
|
156
160
|
```
|
|
161
|
+
|
|
162
|
+
### Non animated modal
|
|
163
|
+
|
|
164
|
+
To explicitly disable animations, set the `hasAnimations` property to `false` on the modal.
|
|
165
|
+
|
|
166
|
+
```ts file="./ModalNotAnimated.tsx"
|
|
167
|
+
|
|
168
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Fragment, useState } from 'react';
|
|
2
|
+
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, ModalVariant } from '@patternfly/react-core';
|
|
3
|
+
|
|
4
|
+
export const ModalNotAnimated: React.FunctionComponent = () => {
|
|
5
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
6
|
+
|
|
7
|
+
const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => {
|
|
8
|
+
setIsModalOpen((prevIsModalOpen) => !prevIsModalOpen);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<Fragment>
|
|
13
|
+
<Button variant="primary" onClick={handleModalToggle}>
|
|
14
|
+
Show no animations modal
|
|
15
|
+
</Button>
|
|
16
|
+
<Modal
|
|
17
|
+
hasAnimations={false}
|
|
18
|
+
variant={ModalVariant.large}
|
|
19
|
+
isOpen={isModalOpen}
|
|
20
|
+
onClose={handleModalToggle}
|
|
21
|
+
aria-labelledby="modal-not-animated-label"
|
|
22
|
+
aria-describedby="modal-not-animated-description"
|
|
23
|
+
elementToFocus="#modal-not-animated-confirm-button"
|
|
24
|
+
>
|
|
25
|
+
<ModalHeader title="Not Animated Modal Header" labelId="modal-not-animated-label" />
|
|
26
|
+
<ModalBody id="modal-not-animated-description">
|
|
27
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
|
28
|
+
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
29
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
|
|
30
|
+
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id
|
|
31
|
+
est laborum.
|
|
32
|
+
</ModalBody>
|
|
33
|
+
<ModalFooter>
|
|
34
|
+
<Button id="modal-not-animated-confirm-button" key="confirm" variant="primary" onClick={handleModalToggle}>
|
|
35
|
+
Confirm
|
|
36
|
+
</Button>
|
|
37
|
+
<Button key="cancel" variant="link" onClick={handleModalToggle}>
|
|
38
|
+
Cancel
|
|
39
|
+
</Button>
|
|
40
|
+
</ModalFooter>
|
|
41
|
+
</Modal>
|
|
42
|
+
</Fragment>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`Modal Content Test description 1`] = `
|
|
4
4
|
<DocumentFragment>
|
|
5
5
|
<div
|
|
6
|
-
class="pf-v6-c-backdrop"
|
|
6
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
7
7
|
>
|
|
8
8
|
<div
|
|
9
9
|
class="pf-v6-l-bullseye"
|
|
@@ -82,7 +82,7 @@ exports[`Modal Content Test description 1`] = `
|
|
|
82
82
|
exports[`Modal Content Test isOpen 1`] = `
|
|
83
83
|
<DocumentFragment>
|
|
84
84
|
<div
|
|
85
|
-
class="pf-v6-c-backdrop"
|
|
85
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
86
86
|
>
|
|
87
87
|
<div
|
|
88
88
|
class="pf-v6-l-bullseye"
|
|
@@ -156,7 +156,7 @@ exports[`Modal Content Test isOpen 1`] = `
|
|
|
156
156
|
exports[`Modal Content Test only body 1`] = `
|
|
157
157
|
<DocumentFragment>
|
|
158
158
|
<div
|
|
159
|
-
class="pf-v6-c-backdrop"
|
|
159
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
160
160
|
>
|
|
161
161
|
<div
|
|
162
162
|
class="pf-v6-l-bullseye"
|
|
@@ -230,7 +230,7 @@ exports[`Modal Content Test only body 1`] = `
|
|
|
230
230
|
exports[`Modal Content Test with footer 1`] = `
|
|
231
231
|
<DocumentFragment>
|
|
232
232
|
<div
|
|
233
|
-
class="pf-v6-c-backdrop"
|
|
233
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
234
234
|
>
|
|
235
235
|
<div
|
|
236
236
|
class="pf-v6-l-bullseye"
|
|
@@ -309,7 +309,7 @@ exports[`Modal Content Test with footer 1`] = `
|
|
|
309
309
|
exports[`Modal Content Test with onclose 1`] = `
|
|
310
310
|
<DocumentFragment>
|
|
311
311
|
<div
|
|
312
|
-
class="pf-v6-c-backdrop"
|
|
312
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
313
313
|
>
|
|
314
314
|
<div
|
|
315
315
|
class="pf-v6-l-bullseye"
|
|
@@ -388,7 +388,7 @@ exports[`Modal Content Test with onclose 1`] = `
|
|
|
388
388
|
exports[`Modal Content test without footer 1`] = `
|
|
389
389
|
<DocumentFragment>
|
|
390
390
|
<div
|
|
391
|
-
class="pf-v6-c-backdrop"
|
|
391
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
392
392
|
>
|
|
393
393
|
<div
|
|
394
394
|
class="pf-v6-l-bullseye"
|
|
@@ -462,7 +462,7 @@ exports[`Modal Content test without footer 1`] = `
|
|
|
462
462
|
exports[`Modal Test with custom footer 1`] = `
|
|
463
463
|
<DocumentFragment>
|
|
464
464
|
<div
|
|
465
|
-
class="pf-v6-c-backdrop"
|
|
465
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
466
466
|
>
|
|
467
467
|
<div
|
|
468
468
|
class="pf-v6-l-bullseye"
|
|
@@ -545,7 +545,7 @@ exports[`Modal Test with custom footer 1`] = `
|
|
|
545
545
|
exports[`Modal Test with custom header 1`] = `
|
|
546
546
|
<DocumentFragment>
|
|
547
547
|
<div
|
|
548
|
-
class="pf-v6-c-backdrop"
|
|
548
|
+
class="pf-v6-c-backdrop pf-m-animate pf-m-show"
|
|
549
549
|
>
|
|
550
550
|
<div
|
|
551
551
|
class="pf-v6-l-bullseye"
|