@patternfly/react-core 6.6.1-prerelease.2 → 6.6.1-prerelease.4
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 +12 -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/Drawer/DrawerCloseButton.d.ts +3 -0
- package/dist/esm/components/Drawer/DrawerCloseButton.d.ts.map +1 -1
- package/dist/esm/components/Drawer/DrawerCloseButton.js +4 -2
- package/dist/esm/components/Drawer/DrawerCloseButton.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 +39 -7
- package/dist/esm/components/Modal/Modal.js.map +1 -1
- package/dist/js/components/Drawer/DrawerCloseButton.d.ts +3 -0
- package/dist/js/components/Drawer/DrawerCloseButton.d.ts.map +1 -1
- package/dist/js/components/Drawer/DrawerCloseButton.js +4 -2
- package/dist/js/components/Drawer/DrawerCloseButton.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 +39 -7
- package/dist/js/components/Modal/Modal.js.map +1 -1
- package/dist/umd/assets/{output-Bon0Nycr.css → output-CB485rDk.css} +18167 -18167
- 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 +2 -2
- package/src/components/Drawer/DrawerCloseButton.tsx +20 -6
- package/src/components/Drawer/__tests__/DrawerCloseButton.test.tsx +39 -0
- package/src/components/Modal/Modal.tsx +42 -7
- package/src/components/Modal/__tests__/Modal.test.tsx +152 -0
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.6.1-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.6.1-prerelease.3","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.6.1-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.6.1-prerelease.3","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.6.1-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.6.1-prerelease.3","private":true}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@patternfly/react-core",
|
|
3
|
-
"version": "6.6.1-prerelease.
|
|
3
|
+
"version": "6.6.1-prerelease.4",
|
|
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",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
"react": "^17 || ^18 || ^19",
|
|
64
64
|
"react-dom": "^17 || ^18 || ^19"
|
|
65
65
|
},
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "1fe30b1dc78a10ff784587cf7ce03d96125fbfb7"
|
|
67
67
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styles from '@patternfly/react-styles/css/components/Drawer/drawer';
|
|
2
2
|
import { css } from '@patternfly/react-styles';
|
|
3
|
-
import { Button } from '../Button';
|
|
3
|
+
import { Button, ButtonProps } from '../Button';
|
|
4
4
|
import RhMicronsCloseIcon from '@patternfly/react-icons/dist/esm/icons/rh-microns-close-icon';
|
|
5
5
|
|
|
6
6
|
export interface DrawerCloseButtonProps extends React.HTMLProps<HTMLDivElement> {
|
|
@@ -10,16 +10,30 @@ export interface DrawerCloseButtonProps extends React.HTMLProps<HTMLDivElement>
|
|
|
10
10
|
onClose?: () => void;
|
|
11
11
|
/** Accessible label for the drawer close button */
|
|
12
12
|
'aria-label'?: string;
|
|
13
|
+
/** Additional properties spread to the close button */
|
|
14
|
+
buttonProps?: Omit<ButtonProps, 'onClick'>;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export const DrawerCloseButton: React.FunctionComponent<DrawerCloseButtonProps> = ({
|
|
16
18
|
className = '',
|
|
17
19
|
onClose = () => undefined as any,
|
|
18
20
|
'aria-label': ariaLabel = 'Close drawer panel',
|
|
21
|
+
buttonProps,
|
|
19
22
|
...props
|
|
20
|
-
}: DrawerCloseButtonProps) =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
}: DrawerCloseButtonProps) => {
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
|
+
const { onClick: _onClick, ...restButtonProps } = (buttonProps ?? {}) as ButtonProps;
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={css(styles.drawerClose, className)} {...props}>
|
|
29
|
+
<Button
|
|
30
|
+
variant="plain"
|
|
31
|
+
onClick={onClose}
|
|
32
|
+
aria-label={ariaLabel}
|
|
33
|
+
icon={<RhMicronsCloseIcon />}
|
|
34
|
+
{...restButtonProps}
|
|
35
|
+
/>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
25
39
|
DrawerCloseButton.displayName = 'DrawerCloseButton';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { ButtonProps } from '../../Button';
|
|
4
|
+
import { DrawerCloseButton } from '../DrawerCloseButton';
|
|
5
|
+
|
|
6
|
+
test('Renders with spread buttonProps', () => {
|
|
7
|
+
render(<DrawerCloseButton buttonProps={{ isDisabled: true }} />);
|
|
8
|
+
expect(screen.getByRole('button')).toBeDisabled();
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test('Calls onClose when clicked', async () => {
|
|
12
|
+
const onClose = jest.fn();
|
|
13
|
+
const user = userEvent.setup();
|
|
14
|
+
|
|
15
|
+
render(<DrawerCloseButton onClose={onClose} buttonProps={{ isDisabled: false }} />);
|
|
16
|
+
await user.click(screen.getByRole('button'));
|
|
17
|
+
expect(onClose).toHaveBeenCalledTimes(1);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('Does not spread onClick from buttonProps but spreads other props', async () => {
|
|
21
|
+
const onClose = jest.fn();
|
|
22
|
+
const buttonOnClick = jest.fn();
|
|
23
|
+
const user = userEvent.setup();
|
|
24
|
+
|
|
25
|
+
render(
|
|
26
|
+
<DrawerCloseButton
|
|
27
|
+
onClose={onClose}
|
|
28
|
+
buttonProps={{ id: 'drawer-close-button', onClick: buttonOnClick } as ButtonProps}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const button = screen.getByRole('button');
|
|
33
|
+
expect(button).toHaveAttribute('id', 'drawer-close-button');
|
|
34
|
+
|
|
35
|
+
await user.click(button);
|
|
36
|
+
|
|
37
|
+
expect(onClose).toHaveBeenCalledTimes(1);
|
|
38
|
+
expect(buttonOnClick).not.toHaveBeenCalled();
|
|
39
|
+
});
|
|
@@ -69,6 +69,7 @@ interface ModalState {
|
|
|
69
69
|
class Modal extends Component<ModalProps, ModalState> {
|
|
70
70
|
static displayName = 'Modal';
|
|
71
71
|
static currentId = 0;
|
|
72
|
+
static openModalStacks: Map<HTMLElement, string[]> = new Map();
|
|
72
73
|
boxId = '';
|
|
73
74
|
backdropId = '';
|
|
74
75
|
|
|
@@ -106,16 +107,46 @@ class Modal extends Component<ModalProps, ModalState> {
|
|
|
106
107
|
return appendTo || document.body;
|
|
107
108
|
};
|
|
108
109
|
|
|
110
|
+
static getStackForTarget(target: HTMLElement): string[] {
|
|
111
|
+
if (!Modal.openModalStacks.has(target)) {
|
|
112
|
+
Modal.openModalStacks.set(target, []);
|
|
113
|
+
}
|
|
114
|
+
return Modal.openModalStacks.get(target)!;
|
|
115
|
+
}
|
|
116
|
+
|
|
109
117
|
toggleSiblingsFromScreenReaders = (hide: boolean) => {
|
|
110
118
|
const { appendTo } = this.props;
|
|
111
119
|
const target: HTMLElement = this.getElement(appendTo);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
|
|
120
|
+
|
|
121
|
+
if (hide) {
|
|
122
|
+
const stack = Modal.getStackForTarget(target);
|
|
123
|
+
if (stack.indexOf(this.backdropId) === -1) {
|
|
124
|
+
stack.push(this.backdropId);
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
const stack = Modal.openModalStacks.get(target);
|
|
128
|
+
if (!stack) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const idx = stack.indexOf(this.backdropId);
|
|
132
|
+
if (idx !== -1) {
|
|
133
|
+
stack.splice(idx, 1);
|
|
134
|
+
}
|
|
135
|
+
if (stack.length === 0) {
|
|
136
|
+
Modal.openModalStacks.delete(target);
|
|
117
137
|
}
|
|
118
138
|
}
|
|
139
|
+
|
|
140
|
+
const stack = Modal.openModalStacks.get(target);
|
|
141
|
+
const activeBackdropId = stack?.length ? stack[stack.length - 1] : null;
|
|
142
|
+
|
|
143
|
+
for (const child of Array.from(target.children)) {
|
|
144
|
+
if (child.hasAttribute('data-popper-placement')) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const shouldHide = activeBackdropId && child.id !== activeBackdropId;
|
|
148
|
+
shouldHide ? child.setAttribute('aria-hidden', 'true') : child.removeAttribute('aria-hidden');
|
|
149
|
+
}
|
|
119
150
|
};
|
|
120
151
|
|
|
121
152
|
isEmpty = (value: string | null | undefined) => value === null || value === undefined || value === '';
|
|
@@ -140,8 +171,10 @@ class Modal extends Component<ModalProps, ModalState> {
|
|
|
140
171
|
this.toggleSiblingsFromScreenReaders(true);
|
|
141
172
|
} else {
|
|
142
173
|
if (prevProps.isOpen !== this.props.isOpen) {
|
|
143
|
-
target.classList.remove(css(styles.backdropOpen));
|
|
144
174
|
this.toggleSiblingsFromScreenReaders(false);
|
|
175
|
+
if (!Modal.openModalStacks.has(target)) {
|
|
176
|
+
target.classList.remove(css(styles.backdropOpen));
|
|
177
|
+
}
|
|
145
178
|
}
|
|
146
179
|
}
|
|
147
180
|
}
|
|
@@ -150,8 +183,10 @@ class Modal extends Component<ModalProps, ModalState> {
|
|
|
150
183
|
const { appendTo } = this.props;
|
|
151
184
|
const target: HTMLElement = this.getElement(appendTo);
|
|
152
185
|
target.removeEventListener('keydown', this.handleEscKeyClick, false);
|
|
153
|
-
target.classList.remove(css(styles.backdropOpen));
|
|
154
186
|
this.toggleSiblingsFromScreenReaders(false);
|
|
187
|
+
if (!Modal.openModalStacks.has(target)) {
|
|
188
|
+
target.classList.remove(css(styles.backdropOpen));
|
|
189
|
+
}
|
|
155
190
|
}
|
|
156
191
|
|
|
157
192
|
render() {
|
|
@@ -64,7 +64,28 @@ const ModalWithAdjacentModal = () => {
|
|
|
64
64
|
);
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
const MultipleOpenModals = () => {
|
|
68
|
+
const [isFirstOpen, setIsFirstOpen] = useState(true);
|
|
69
|
+
const [isSecondOpen, setIsSecondOpen] = useState(false);
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<aside>Aside sibling</aside>
|
|
74
|
+
<Modal isOpen={isFirstOpen} appendTo={target} onClose={() => setIsFirstOpen(false)} aria-label="First modal">
|
|
75
|
+
<button onClick={() => setIsSecondOpen(true)}>Open second modal</button>
|
|
76
|
+
</Modal>
|
|
77
|
+
<Modal isOpen={isSecondOpen} appendTo={target} onClose={() => setIsSecondOpen(false)} aria-label="Second modal">
|
|
78
|
+
Second modal content
|
|
79
|
+
</Modal>
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
67
84
|
describe('Modal', () => {
|
|
85
|
+
beforeEach(() => {
|
|
86
|
+
Modal.openModalStacks = new Map();
|
|
87
|
+
});
|
|
88
|
+
|
|
68
89
|
test('Modal creates a container element once for div', () => {
|
|
69
90
|
render(<Modal {...props} />);
|
|
70
91
|
expect(document.createElement).toHaveBeenCalledWith('div');
|
|
@@ -181,4 +202,135 @@ describe('Modal', () => {
|
|
|
181
202
|
'pf-v6-l-bullseye'
|
|
182
203
|
);
|
|
183
204
|
});
|
|
205
|
+
|
|
206
|
+
test('backdropOpen class remains when closing one of multiple open modals', async () => {
|
|
207
|
+
const user = userEvent.setup();
|
|
208
|
+
|
|
209
|
+
render(<MultipleOpenModals />, { container: document.body.appendChild(target) });
|
|
210
|
+
|
|
211
|
+
await user.click(screen.getByRole('button', { name: 'Open second modal' }));
|
|
212
|
+
|
|
213
|
+
expect(target).toHaveClass(css(styles.backdropOpen));
|
|
214
|
+
|
|
215
|
+
const closeButtons = screen.getAllByRole('button', { name: 'Close', hidden: true });
|
|
216
|
+
await user.click(closeButtons[closeButtons.length - 1]);
|
|
217
|
+
|
|
218
|
+
expect(target).toHaveClass(css(styles.backdropOpen));
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('backdropOpen class is removed when all modals are closed', async () => {
|
|
222
|
+
const user = userEvent.setup();
|
|
223
|
+
|
|
224
|
+
render(<MultipleOpenModals />, { container: document.body.appendChild(target) });
|
|
225
|
+
|
|
226
|
+
await user.click(screen.getByRole('button', { name: 'Open second modal' }));
|
|
227
|
+
|
|
228
|
+
const closeButtons = screen.getAllByRole('button', { name: 'Close', hidden: true });
|
|
229
|
+
await user.click(closeButtons[closeButtons.length - 1]);
|
|
230
|
+
await user.click(screen.getByRole('button', { name: 'Close' }));
|
|
231
|
+
|
|
232
|
+
expect(target).not.toHaveClass(css(styles.backdropOpen));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('only the most recent modal does not have aria-hidden when multiple modals are open', async () => {
|
|
236
|
+
const user = userEvent.setup();
|
|
237
|
+
|
|
238
|
+
render(<MultipleOpenModals />, { container: document.body.appendChild(target) });
|
|
239
|
+
|
|
240
|
+
const firstBackdrop = screen.getByLabelText('First modal').closest('[class*="backdrop"]');
|
|
241
|
+
|
|
242
|
+
await user.click(screen.getByRole('button', { name: 'Open second modal' }));
|
|
243
|
+
|
|
244
|
+
const secondBackdrop = screen.getByLabelText('Second modal').closest('[class*="backdrop"]');
|
|
245
|
+
|
|
246
|
+
expect(firstBackdrop).toHaveAttribute('aria-hidden', 'true');
|
|
247
|
+
expect(secondBackdrop).not.toHaveAttribute('aria-hidden');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('closing the active modal reveals the previous modal', async () => {
|
|
251
|
+
const user = userEvent.setup();
|
|
252
|
+
|
|
253
|
+
render(<MultipleOpenModals />, { container: document.body.appendChild(target) });
|
|
254
|
+
|
|
255
|
+
await user.click(screen.getByRole('button', { name: 'Open second modal' }));
|
|
256
|
+
|
|
257
|
+
const firstBackdrop = screen
|
|
258
|
+
.getByLabelText('First modal', { selector: '[role="dialog"]' })
|
|
259
|
+
.closest('[class*="backdrop"]');
|
|
260
|
+
|
|
261
|
+
expect(firstBackdrop).toHaveAttribute('aria-hidden', 'true');
|
|
262
|
+
|
|
263
|
+
const closeButtons = screen.getAllByRole('button', { name: 'Close', hidden: true });
|
|
264
|
+
await user.click(closeButtons[closeButtons.length - 1]);
|
|
265
|
+
|
|
266
|
+
expect(firstBackdrop).not.toHaveAttribute('aria-hidden');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('modals with different appendTo targets have independent stacks', async () => {
|
|
270
|
+
const user = userEvent.setup();
|
|
271
|
+
const targetA = document.createElement('div');
|
|
272
|
+
const targetB = document.createElement('div');
|
|
273
|
+
document.body.appendChild(targetA);
|
|
274
|
+
document.body.appendChild(targetB);
|
|
275
|
+
|
|
276
|
+
const siblingA = document.createElement('aside');
|
|
277
|
+
siblingA.textContent = 'Sibling A';
|
|
278
|
+
targetA.appendChild(siblingA);
|
|
279
|
+
|
|
280
|
+
const siblingB = document.createElement('aside');
|
|
281
|
+
siblingB.textContent = 'Sibling B';
|
|
282
|
+
targetB.appendChild(siblingB);
|
|
283
|
+
|
|
284
|
+
const DistinctTargetModals = () => {
|
|
285
|
+
const [isAOpen, setIsAOpen] = useState(true);
|
|
286
|
+
const [isBOpen, setIsBOpen] = useState(true);
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<>
|
|
290
|
+
<Modal isOpen={isAOpen} appendTo={targetA} onClose={() => setIsAOpen(false)} aria-label="Modal A">
|
|
291
|
+
Modal A content
|
|
292
|
+
</Modal>
|
|
293
|
+
<Modal isOpen={isBOpen} appendTo={targetB} onClose={() => setIsBOpen(false)} aria-label="Modal B">
|
|
294
|
+
Modal B content
|
|
295
|
+
</Modal>
|
|
296
|
+
</>
|
|
297
|
+
);
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
render(<DistinctTargetModals />);
|
|
301
|
+
|
|
302
|
+
expect(siblingA).toHaveAttribute('aria-hidden', 'true');
|
|
303
|
+
expect(siblingB).toHaveAttribute('aria-hidden', 'true');
|
|
304
|
+
expect(targetA).toHaveClass(css(styles.backdropOpen));
|
|
305
|
+
expect(targetB).toHaveClass(css(styles.backdropOpen));
|
|
306
|
+
|
|
307
|
+
const closeButtons = screen.getAllByRole('button', { name: 'Close', hidden: true });
|
|
308
|
+
await user.click(closeButtons[1]);
|
|
309
|
+
|
|
310
|
+
expect(targetB).not.toHaveClass(css(styles.backdropOpen));
|
|
311
|
+
expect(siblingB).not.toHaveAttribute('aria-hidden');
|
|
312
|
+
|
|
313
|
+
expect(targetA).toHaveClass(css(styles.backdropOpen));
|
|
314
|
+
expect(siblingA).toHaveAttribute('aria-hidden', 'true');
|
|
315
|
+
|
|
316
|
+
document.body.removeChild(targetA);
|
|
317
|
+
document.body.removeChild(targetB);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('unmounting a never-opened modal with a custom target does not leak a stack entry', () => {
|
|
321
|
+
const customTarget = document.createElement('div');
|
|
322
|
+
document.body.appendChild(customTarget);
|
|
323
|
+
|
|
324
|
+
const { unmount } = render(
|
|
325
|
+
<Modal isOpen={false} appendTo={customTarget} onClose={() => {}}>
|
|
326
|
+
Never opened
|
|
327
|
+
</Modal>
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
unmount();
|
|
331
|
+
|
|
332
|
+
expect(Modal.openModalStacks.has(customTarget)).toBe(false);
|
|
333
|
+
|
|
334
|
+
document.body.removeChild(customTarget);
|
|
335
|
+
});
|
|
184
336
|
});
|