@patternfly/react-core 6.1.0-prerelease.6 → 6.1.0-prerelease.7

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.
Files changed (157) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/package.json +1 -1
  3. package/deprecated/package.json +1 -1
  4. package/dist/dynamic/components/AboutModal/package.json +1 -1
  5. package/dist/dynamic/components/Accordion/package.json +1 -1
  6. package/dist/dynamic/components/ActionList/package.json +1 -1
  7. package/dist/dynamic/components/Alert/package.json +1 -1
  8. package/dist/dynamic/components/Avatar/package.json +1 -1
  9. package/dist/dynamic/components/BackToTop/package.json +1 -1
  10. package/dist/dynamic/components/Backdrop/package.json +1 -1
  11. package/dist/dynamic/components/BackgroundImage/package.json +1 -1
  12. package/dist/dynamic/components/Badge/package.json +1 -1
  13. package/dist/dynamic/components/Banner/package.json +1 -1
  14. package/dist/dynamic/components/Brand/package.json +1 -1
  15. package/dist/dynamic/components/Breadcrumb/package.json +1 -1
  16. package/dist/dynamic/components/Button/package.json +1 -1
  17. package/dist/dynamic/components/CalendarMonth/package.json +1 -1
  18. package/dist/dynamic/components/Card/package.json +1 -1
  19. package/dist/dynamic/components/Checkbox/package.json +1 -1
  20. package/dist/dynamic/components/ClipboardCopy/package.json +1 -1
  21. package/dist/dynamic/components/CodeBlock/package.json +1 -1
  22. package/dist/dynamic/components/Content/package.json +1 -1
  23. package/dist/dynamic/components/DataList/package.json +1 -1
  24. package/dist/dynamic/components/DatePicker/package.json +1 -1
  25. package/dist/dynamic/components/DescriptionList/package.json +1 -1
  26. package/dist/dynamic/components/Divider/package.json +1 -1
  27. package/dist/dynamic/components/Drawer/package.json +1 -1
  28. package/dist/dynamic/components/Dropdown/package.json +1 -1
  29. package/dist/dynamic/components/DualListSelector/package.json +1 -1
  30. package/dist/dynamic/components/EmptyState/package.json +1 -1
  31. package/dist/dynamic/components/ExpandableSection/package.json +1 -1
  32. package/dist/dynamic/components/FileUpload/package.json +1 -1
  33. package/dist/dynamic/components/Form/package.json +1 -1
  34. package/dist/dynamic/components/FormSelect/package.json +1 -1
  35. package/dist/dynamic/components/HelperText/package.json +1 -1
  36. package/dist/dynamic/components/Hint/package.json +1 -1
  37. package/dist/dynamic/components/Icon/package.json +1 -1
  38. package/dist/dynamic/components/InputGroup/package.json +1 -1
  39. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  40. package/dist/dynamic/components/Label/package.json +1 -1
  41. package/dist/dynamic/components/List/package.json +1 -1
  42. package/dist/dynamic/components/LoginPage/package.json +1 -1
  43. package/dist/dynamic/components/Masthead/package.json +1 -1
  44. package/dist/dynamic/components/Menu/package.json +1 -1
  45. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  46. package/dist/dynamic/components/Modal/package.json +1 -1
  47. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  48. package/dist/dynamic/components/Nav/package.json +1 -1
  49. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  50. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  51. package/dist/dynamic/components/NumberInput/package.json +1 -1
  52. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  53. package/dist/dynamic/components/Page/package.json +1 -1
  54. package/dist/dynamic/components/Pagination/package.json +1 -1
  55. package/dist/dynamic/components/Panel/package.json +1 -1
  56. package/dist/dynamic/components/Popover/package.json +1 -1
  57. package/dist/dynamic/components/Progress/package.json +1 -1
  58. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  59. package/dist/dynamic/components/Radio/package.json +1 -1
  60. package/dist/dynamic/components/SearchInput/package.json +1 -1
  61. package/dist/dynamic/components/Select/package.json +1 -1
  62. package/dist/dynamic/components/Sidebar/package.json +1 -1
  63. package/dist/dynamic/components/SimpleList/package.json +1 -1
  64. package/dist/dynamic/components/Skeleton/package.json +1 -1
  65. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  66. package/dist/dynamic/components/Slider/package.json +1 -1
  67. package/dist/dynamic/components/Spinner/package.json +1 -1
  68. package/dist/dynamic/components/Switch/package.json +1 -1
  69. package/dist/dynamic/components/Tabs/package.json +1 -1
  70. package/dist/dynamic/components/TextArea/package.json +1 -1
  71. package/dist/dynamic/components/TextInput/package.json +1 -1
  72. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  73. package/dist/dynamic/components/TimePicker/package.json +1 -1
  74. package/dist/dynamic/components/Timestamp/package.json +1 -1
  75. package/dist/dynamic/components/Title/package.json +1 -1
  76. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  77. package/dist/dynamic/components/Toolbar/package.json +1 -1
  78. package/dist/dynamic/components/Tooltip/package.json +1 -1
  79. package/dist/dynamic/components/TreeView/package.json +1 -1
  80. package/dist/dynamic/components/Truncate/package.json +1 -1
  81. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  82. package/dist/dynamic/components/Wizard/package.json +1 -1
  83. package/dist/dynamic/deprecated/components/Chip/package.json +1 -1
  84. package/dist/dynamic/deprecated/components/DragDrop/package.json +1 -1
  85. package/dist/dynamic/deprecated/components/DualListSelector/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/Modal/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/Tile/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/package.json +1 -1
  90. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  91. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  92. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  93. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  94. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  95. package/dist/dynamic/helpers/constants/package.json +1 -1
  96. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  97. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  98. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  99. package/dist/dynamic/helpers/package.json +1 -1
  100. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  101. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  102. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  103. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  104. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  105. package/dist/dynamic/helpers/util/package.json +1 -1
  106. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  107. package/dist/dynamic/layouts/Flex/package.json +1 -1
  108. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  109. package/dist/dynamic/layouts/Grid/package.json +1 -1
  110. package/dist/dynamic/layouts/Level/package.json +1 -1
  111. package/dist/dynamic/layouts/Split/package.json +1 -1
  112. package/dist/dynamic/layouts/Stack/package.json +1 -1
  113. package/dist/dynamic/styles/package.json +1 -1
  114. package/dist/esm/components/Modal/Modal.d.ts +2 -2
  115. package/dist/esm/components/Modal/Modal.d.ts.map +1 -1
  116. package/dist/esm/components/Modal/Modal.js +13 -20
  117. package/dist/esm/components/Modal/Modal.js.map +1 -1
  118. package/dist/esm/components/Modal/ModalContent.d.ts +2 -0
  119. package/dist/esm/components/Modal/ModalContent.d.ts.map +1 -1
  120. package/dist/esm/components/Modal/ModalContent.js +2 -2
  121. package/dist/esm/components/Modal/ModalContent.js.map +1 -1
  122. package/dist/esm/deprecated/components/Modal/Modal.d.ts +2 -2
  123. package/dist/esm/deprecated/components/Modal/Modal.d.ts.map +1 -1
  124. package/dist/esm/deprecated/components/Modal/Modal.js +13 -20
  125. package/dist/esm/deprecated/components/Modal/Modal.js.map +1 -1
  126. package/dist/esm/deprecated/components/Modal/ModalContent.d.ts +2 -0
  127. package/dist/esm/deprecated/components/Modal/ModalContent.d.ts.map +1 -1
  128. package/dist/esm/deprecated/components/Modal/ModalContent.js +2 -2
  129. package/dist/esm/deprecated/components/Modal/ModalContent.js.map +1 -1
  130. package/dist/js/components/Modal/Modal.d.ts +2 -2
  131. package/dist/js/components/Modal/Modal.d.ts.map +1 -1
  132. package/dist/js/components/Modal/Modal.js +13 -20
  133. package/dist/js/components/Modal/Modal.js.map +1 -1
  134. package/dist/js/components/Modal/ModalContent.d.ts +2 -0
  135. package/dist/js/components/Modal/ModalContent.d.ts.map +1 -1
  136. package/dist/js/components/Modal/ModalContent.js +2 -2
  137. package/dist/js/components/Modal/ModalContent.js.map +1 -1
  138. package/dist/js/deprecated/components/Modal/Modal.d.ts +2 -2
  139. package/dist/js/deprecated/components/Modal/Modal.d.ts.map +1 -1
  140. package/dist/js/deprecated/components/Modal/Modal.js +13 -20
  141. package/dist/js/deprecated/components/Modal/Modal.js.map +1 -1
  142. package/dist/js/deprecated/components/Modal/ModalContent.d.ts +2 -0
  143. package/dist/js/deprecated/components/Modal/ModalContent.d.ts.map +1 -1
  144. package/dist/js/deprecated/components/Modal/ModalContent.js +2 -2
  145. package/dist/js/deprecated/components/Modal/ModalContent.js.map +1 -1
  146. package/dist/umd/assets/{output-C2gozh3-.css → output-DacqxObv.css} +3137 -3137
  147. package/dist/umd/react-core.min.js +2 -2
  148. package/helpers/package.json +1 -1
  149. package/layouts/package.json +1 -1
  150. package/next/package.json +1 -1
  151. package/package.json +2 -2
  152. package/src/components/Modal/Modal.tsx +13 -18
  153. package/src/components/Modal/ModalContent.tsx +4 -1
  154. package/src/components/Modal/__tests__/Modal.test.tsx +45 -0
  155. package/src/deprecated/components/Modal/Modal.tsx +13 -18
  156. package/src/deprecated/components/Modal/ModalContent.tsx +4 -1
  157. package/src/deprecated/components/Modal/__tests__/Modal.test.tsx +28 -0
@@ -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.1.0-prerelease.5","private":true}
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.1.0-prerelease.6","private":true}
@@ -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.1.0-prerelease.5","private":true}
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.1.0-prerelease.6","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.1.0-prerelease.5","private":true}
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.1.0-prerelease.6","private":true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@patternfly/react-core",
3
- "version": "6.1.0-prerelease.6",
3
+ "version": "6.1.0-prerelease.7",
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",
64
64
  "react-dom": "^17 || ^18"
65
65
  },
66
- "gitHead": "53da56fdc7b7b149101c9d1156b5999dda3ba579"
66
+ "gitHead": "155162dc873989fa335d181cf898bdd3b2bb2873"
67
67
  }
@@ -60,7 +60,6 @@ export enum ModalVariant {
60
60
  }
61
61
 
62
62
  interface ModalState {
63
- container: HTMLElement;
64
63
  ouiaStateId: string;
65
64
  }
66
65
 
@@ -68,6 +67,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
68
67
  static displayName = 'Modal';
69
68
  static currentId = 0;
70
69
  boxId = '';
70
+ backdropId = '';
71
71
 
72
72
  static defaultProps: PickOptional<ModalProps> = {
73
73
  isOpen: false,
@@ -80,10 +80,11 @@ class Modal extends React.Component<ModalProps, ModalState> {
80
80
  constructor(props: ModalProps) {
81
81
  super(props);
82
82
  const boxIdNum = Modal.currentId++;
83
+ const backdropId = boxIdNum + 1;
83
84
  this.boxId = props.id || `pf-modal-part-${boxIdNum}`;
85
+ this.backdropId = `pf-modal-part-${backdropId}`;
84
86
 
85
87
  this.state = {
86
- container: undefined,
87
88
  ouiaStateId: getDefaultOUIAId(Modal.displayName, props.variant)
88
89
  };
89
90
  }
@@ -107,7 +108,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
107
108
  const target: HTMLElement = this.getElement(appendTo);
108
109
  const bodyChildren = target.children;
109
110
  for (const child of Array.from(bodyChildren)) {
110
- if (child !== this.state.container) {
111
+ if (child.id !== this.backdropId) {
111
112
  hide ? child.setAttribute('aria-hidden', '' + hide) : child.removeAttribute('aria-hidden');
112
113
  }
113
114
  }
@@ -118,36 +119,31 @@ class Modal extends React.Component<ModalProps, ModalState> {
118
119
  componentDidMount() {
119
120
  const { appendTo } = this.props;
120
121
  const target: HTMLElement = this.getElement(appendTo);
121
- const container = document.createElement('div');
122
- this.setState({ container });
123
- target.appendChild(container);
124
122
  target.addEventListener('keydown', this.handleEscKeyClick, false);
125
123
 
126
124
  if (this.props.isOpen) {
127
125
  target.classList.add(css(styles.backdropOpen));
128
- } else {
129
- target.classList.remove(css(styles.backdropOpen));
126
+ this.toggleSiblingsFromScreenReaders(true);
130
127
  }
131
128
  }
132
129
 
133
- componentDidUpdate() {
130
+ componentDidUpdate(prevProps: ModalProps) {
134
131
  const { appendTo } = this.props;
135
132
  const target: HTMLElement = this.getElement(appendTo);
136
133
  if (this.props.isOpen) {
137
134
  target.classList.add(css(styles.backdropOpen));
138
135
  this.toggleSiblingsFromScreenReaders(true);
139
136
  } else {
140
- target.classList.remove(css(styles.backdropOpen));
141
- this.toggleSiblingsFromScreenReaders(false);
137
+ if (prevProps.isOpen !== this.props.isOpen) {
138
+ target.classList.remove(css(styles.backdropOpen));
139
+ this.toggleSiblingsFromScreenReaders(false);
140
+ }
142
141
  }
143
142
  }
144
143
 
145
144
  componentWillUnmount() {
146
145
  const { appendTo } = this.props;
147
146
  const target: HTMLElement = this.getElement(appendTo);
148
- if (this.state.container) {
149
- target.removeChild(this.state.container);
150
- }
151
147
  target.removeEventListener('keydown', this.handleEscKeyClick, false);
152
148
  target.classList.remove(css(styles.backdropOpen));
153
149
  this.toggleSiblingsFromScreenReaders(false);
@@ -155,7 +151,6 @@ class Modal extends React.Component<ModalProps, ModalState> {
155
151
 
156
152
  render() {
157
153
  const {
158
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
159
154
  appendTo,
160
155
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
161
156
  onEscapePress,
@@ -168,15 +163,15 @@ class Modal extends React.Component<ModalProps, ModalState> {
168
163
  elementToFocus,
169
164
  ...props
170
165
  } = this.props;
171
- const { container } = this.state;
172
166
 
173
- if (!canUseDOM || !container) {
167
+ if (!canUseDOM || !this.getElement(appendTo)) {
174
168
  return null;
175
169
  }
176
170
 
177
171
  return ReactDOM.createPortal(
178
172
  <ModalContent
179
173
  boxId={this.boxId}
174
+ backdropId={this.backdropId}
180
175
  aria-label={ariaLabel}
181
176
  aria-describedby={ariaDescribedby}
182
177
  aria-labelledby={ariaLabelledby}
@@ -187,7 +182,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
187
182
  backdropClassName={props.backdropClassName}
188
183
  {...props}
189
184
  />,
190
- container
185
+ this.getElement(appendTo)
191
186
  ) as React.ReactElement;
192
187
  }
193
188
  }
@@ -16,6 +16,8 @@ export interface ModalContentProps extends OUIAProps {
16
16
  'aria-labelledby'?: string;
17
17
  /** Id of the modal box container. */
18
18
  boxId: string;
19
+ /** Id of the backdrop. */
20
+ backdropId?: string;
19
21
  /** Content rendered inside the modal. */
20
22
  children: React.ReactNode;
21
23
  /** Additional classes added to the modal box. */
@@ -63,6 +65,7 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
63
65
  width,
64
66
  maxWidth,
65
67
  boxId,
68
+ backdropId,
66
69
  disableFocusTrap = false,
67
70
  ouiaId,
68
71
  ouiaSafe = true,
@@ -109,7 +112,7 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
109
112
  </ModalBox>
110
113
  );
111
114
  return (
112
- <Backdrop className={css(backdropClassName)}>
115
+ <Backdrop className={css(backdropClassName)} id={backdropId}>
113
116
  <FocusTrap
114
117
  active={!disableFocusTrap}
115
118
  focusTrapOptions={{
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
 
3
3
  import { render, screen } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
+ import '@testing-library/jest-dom';
5
6
 
6
7
  import { css } from '../../../../../react-styles/dist/js';
7
8
  import styles from '@patternfly/react-styles/css/components/Backdrop/backdrop';
@@ -38,6 +39,32 @@ const ModalWithSiblings = () => {
38
39
  );
39
40
  };
40
41
 
42
+ const ModalWithAdjacentModal = () => {
43
+ const [isOpen, setIsOpen] = React.useState(true);
44
+ const [isModalMounted, setIsModalMounted] = React.useState(true);
45
+ const modalProps = { ...props, isOpen, appendTo: target, onClose: () => setIsOpen(false) };
46
+
47
+ return (
48
+ <>
49
+ <aside>Aside sibling</aside>
50
+ <article>Section sibling</article>
51
+ {isModalMounted && (
52
+ <>
53
+ <Modal {...modalProps}>
54
+ <button onClick={() => setIsModalMounted(false)}>Unmount Modal</button>
55
+ </Modal>
56
+ <Modal isOpen={false} onClose={() => {}}>
57
+ Modal closed for test
58
+ </Modal>
59
+ <Modal isOpen={false} onClose={() => {}}>
60
+ modal closed for test
61
+ </Modal>
62
+ </>
63
+ )}
64
+ </>
65
+ );
66
+ };
67
+
41
68
  describe('Modal', () => {
42
69
  test('Modal creates a container element once for div', () => {
43
70
  render(<Modal {...props} />);
@@ -128,4 +155,22 @@ describe('Modal', () => {
128
155
  expect(asideSibling).not.toHaveAttribute('aria-hidden');
129
156
  expect(articleSibling).not.toHaveAttribute('aria-hidden');
130
157
  });
158
+
159
+ test('modal siblings have the aria-hidden attribute when it has adjacent modals', async () => {
160
+ const user = userEvent.setup();
161
+
162
+ render(<ModalWithAdjacentModal />, { container: document.body.appendChild(target) });
163
+
164
+ const asideSibling = screen.getByRole('complementary', { hidden: true });
165
+ const articleSibling = screen.getByRole('article', { hidden: true });
166
+ const unmountButton = screen.getByRole('button', { name: 'Unmount Modal' });
167
+
168
+ expect(asideSibling).toHaveAttribute('aria-hidden');
169
+ expect(articleSibling).toHaveAttribute('aria-hidden');
170
+
171
+ await user.click(unmountButton);
172
+
173
+ expect(asideSibling).not.toHaveAttribute('aria-hidden');
174
+ expect(articleSibling).not.toHaveAttribute('aria-hidden');
175
+ });
131
176
  });
@@ -92,7 +92,6 @@ export enum ModalVariant {
92
92
  }
93
93
 
94
94
  interface ModalState {
95
- container: HTMLElement;
96
95
  ouiaStateId: string;
97
96
  }
98
97
 
@@ -102,6 +101,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
102
101
  boxId = '';
103
102
  labelId = '';
104
103
  descriptorId = '';
104
+ backdropId = '';
105
105
 
106
106
  static defaultProps: PickOptional<ModalProps> = {
107
107
  className: '',
@@ -128,12 +128,13 @@ class Modal extends React.Component<ModalProps, ModalState> {
128
128
  const boxIdNum = Modal.currentId++;
129
129
  const labelIdNum = boxIdNum + 1;
130
130
  const descriptorIdNum = boxIdNum + 2;
131
+ const backdropIdNum = boxIdNum + 3;
131
132
  this.boxId = props.id || `pf-modal-part-${boxIdNum}`;
132
133
  this.labelId = `pf-modal-part-${labelIdNum}`;
133
134
  this.descriptorId = `pf-modal-part-${descriptorIdNum}`;
135
+ this.backdropId = `pf-modal-part-${backdropIdNum}`;
134
136
 
135
137
  this.state = {
136
- container: undefined,
137
138
  ouiaStateId: getDefaultOUIAId(Modal.displayName, props.variant)
138
139
  };
139
140
  }
@@ -157,7 +158,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
157
158
  const target: HTMLElement = this.getElement(appendTo);
158
159
  const bodyChildren = target.children;
159
160
  for (const child of Array.from(bodyChildren)) {
160
- if (child !== this.state.container) {
161
+ if (child.id !== this.backdropId) {
161
162
  hide ? child.setAttribute('aria-hidden', '' + hide) : child.removeAttribute('aria-hidden');
162
163
  }
163
164
  }
@@ -175,15 +176,11 @@ class Modal extends React.Component<ModalProps, ModalState> {
175
176
  header
176
177
  } = this.props;
177
178
  const target: HTMLElement = this.getElement(appendTo);
178
- const container = document.createElement('div');
179
- this.setState({ container });
180
- target.appendChild(container);
181
179
  target.addEventListener('keydown', this.handleEscKeyClick, false);
182
180
 
183
181
  if (this.props.isOpen) {
184
182
  target.classList.add(css(styles.backdropOpen));
185
- } else {
186
- target.classList.remove(css(styles.backdropOpen));
183
+ this.toggleSiblingsFromScreenReaders(true);
187
184
  }
188
185
 
189
186
  if (!title && this.isEmpty(ariaLabel) && this.isEmpty(ariaLabelledby)) {
@@ -199,24 +196,23 @@ class Modal extends React.Component<ModalProps, ModalState> {
199
196
  }
200
197
  }
201
198
 
202
- componentDidUpdate() {
199
+ componentDidUpdate(prevProps: ModalProps) {
203
200
  const { appendTo } = this.props;
204
201
  const target: HTMLElement = this.getElement(appendTo);
205
202
  if (this.props.isOpen) {
206
203
  target.classList.add(css(styles.backdropOpen));
207
204
  this.toggleSiblingsFromScreenReaders(true);
208
205
  } else {
209
- target.classList.remove(css(styles.backdropOpen));
210
- this.toggleSiblingsFromScreenReaders(false);
206
+ if (prevProps.isOpen !== this.props.isOpen) {
207
+ target.classList.remove(css(styles.backdropOpen));
208
+ this.toggleSiblingsFromScreenReaders(false);
209
+ }
211
210
  }
212
211
  }
213
212
 
214
213
  componentWillUnmount() {
215
214
  const { appendTo } = this.props;
216
215
  const target: HTMLElement = this.getElement(appendTo);
217
- if (this.state.container) {
218
- target.removeChild(this.state.container);
219
- }
220
216
  target.removeEventListener('keydown', this.handleEscKeyClick, false);
221
217
  target.classList.remove(css(styles.backdropOpen));
222
218
  this.toggleSiblingsFromScreenReaders(false);
@@ -224,7 +220,6 @@ class Modal extends React.Component<ModalProps, ModalState> {
224
220
 
225
221
  render() {
226
222
  const {
227
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
228
223
  appendTo,
229
224
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
230
225
  onEscapePress,
@@ -242,9 +237,8 @@ class Modal extends React.Component<ModalProps, ModalState> {
242
237
  elementToFocus,
243
238
  ...props
244
239
  } = this.props;
245
- const { container } = this.state;
246
240
 
247
- if (!canUseDOM || !container) {
241
+ if (!canUseDOM || !this.getElement(appendTo)) {
248
242
  return null;
249
243
  }
250
244
 
@@ -254,6 +248,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
254
248
  boxId={this.boxId}
255
249
  labelId={this.labelId}
256
250
  descriptorId={this.descriptorId}
251
+ backdropId={this.backdropId}
257
252
  title={title}
258
253
  titleIconVariant={titleIconVariant}
259
254
  titleLabel={titleLabel}
@@ -267,7 +262,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
267
262
  position={position}
268
263
  elementToFocus={elementToFocus}
269
264
  />,
270
- container
265
+ this.getElement(appendTo)
271
266
  ) as React.ReactElement;
272
267
  }
273
268
  }
@@ -25,6 +25,8 @@ export interface ModalContentProps extends OUIAProps {
25
25
  'aria-label'?: string;
26
26
  /** Id to use for the modal box label. */
27
27
  'aria-labelledby'?: string | null;
28
+ /** Id of the backdrop. */
29
+ backdropId?: string;
28
30
  /** Accessible label applied to the modal box body. This should be used to communicate
29
31
  * important information about the modal box body div element if needed, such as that it
30
32
  * is scrollable.
@@ -117,6 +119,7 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
117
119
  maxWidth,
118
120
  boxId,
119
121
  labelId,
122
+ backdropId,
120
123
  descriptorId,
121
124
  disableFocusTrap = false,
122
125
  hasNoBodyWrapper = false,
@@ -202,7 +205,7 @@ export const ModalContent: React.FunctionComponent<ModalContentProps> = ({
202
205
  </ModalBox>
203
206
  );
204
207
  return (
205
- <Backdrop>
208
+ <Backdrop id={backdropId}>
206
209
  <FocusTrap
207
210
  active={!disableFocusTrap}
208
211
  focusTrapOptions={{
@@ -2,6 +2,7 @@ import * as React from 'react';
2
2
 
3
3
  import { render, screen } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
+ import '@testing-library/jest-dom';
5
6
 
6
7
  import { css } from '../../../../../../react-styles/dist/js';
7
8
  import styles from '@patternfly/react-styles/css/components/Backdrop/backdrop';
@@ -39,6 +40,32 @@ const ModalWithSiblings = () => {
39
40
  );
40
41
  };
41
42
 
43
+ const ModalWithAdjacentModal = () => {
44
+ const [isOpen, setIsOpen] = React.useState(true);
45
+ const [isModalMounted, setIsModalMounted] = React.useState(true);
46
+ const modalProps = { ...props, isOpen, appendTo: target, onClose: () => setIsOpen(false) };
47
+
48
+ return (
49
+ <>
50
+ <aside>Aside sibling</aside>
51
+ <article>Section sibling</article>
52
+ {isModalMounted && (
53
+ <>
54
+ <Modal {...modalProps}>
55
+ <button onClick={() => setIsModalMounted(false)}>Unmount Modal</button>
56
+ </Modal>
57
+ <Modal isOpen={false} onClose={() => {}}>
58
+ Modal closed for test
59
+ </Modal>
60
+ <Modal isOpen={false} onClose={() => {}}>
61
+ modal closed for test
62
+ </Modal>
63
+ </>
64
+ )}
65
+ </>
66
+ );
67
+ };
68
+
42
69
  describe('Modal', () => {
43
70
  test('Modal creates a container element once for div', () => {
44
71
  render(<Modal {...props} />);
@@ -154,6 +181,7 @@ describe('Modal', () => {
154
181
  expect(asideSibling).not.toHaveAttribute('aria-hidden');
155
182
  expect(articleSibling).not.toHaveAttribute('aria-hidden');
156
183
  });
184
+
157
185
  test('The modalBoxBody has no aria-label when bodyAriaLabel is not passed', () => {
158
186
  const props = {
159
187
  isOpen: true