@patternfly/react-core 6.6.1-prerelease.3 → 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.
Files changed (136) 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/Compass/package.json +1 -1
  23. package/dist/dynamic/components/Content/package.json +1 -1
  24. package/dist/dynamic/components/DataList/package.json +1 -1
  25. package/dist/dynamic/components/DatePicker/package.json +1 -1
  26. package/dist/dynamic/components/DescriptionList/package.json +1 -1
  27. package/dist/dynamic/components/Divider/package.json +1 -1
  28. package/dist/dynamic/components/Drawer/package.json +1 -1
  29. package/dist/dynamic/components/Dropdown/package.json +1 -1
  30. package/dist/dynamic/components/DualListSelector/package.json +1 -1
  31. package/dist/dynamic/components/EmptyState/package.json +1 -1
  32. package/dist/dynamic/components/ExpandableSection/package.json +1 -1
  33. package/dist/dynamic/components/FileUpload/package.json +1 -1
  34. package/dist/dynamic/components/Form/package.json +1 -1
  35. package/dist/dynamic/components/FormSelect/package.json +1 -1
  36. package/dist/dynamic/components/HelperText/package.json +1 -1
  37. package/dist/dynamic/components/Hero/package.json +1 -1
  38. package/dist/dynamic/components/Hint/package.json +1 -1
  39. package/dist/dynamic/components/Icon/package.json +1 -1
  40. package/dist/dynamic/components/InputGroup/package.json +1 -1
  41. package/dist/dynamic/components/JumpLinks/package.json +1 -1
  42. package/dist/dynamic/components/Label/package.json +1 -1
  43. package/dist/dynamic/components/List/package.json +1 -1
  44. package/dist/dynamic/components/LoginPage/package.json +1 -1
  45. package/dist/dynamic/components/Masthead/package.json +1 -1
  46. package/dist/dynamic/components/Menu/package.json +1 -1
  47. package/dist/dynamic/components/MenuToggle/package.json +1 -1
  48. package/dist/dynamic/components/Modal/package.json +1 -1
  49. package/dist/dynamic/components/MultipleFileUpload/package.json +1 -1
  50. package/dist/dynamic/components/Nav/package.json +1 -1
  51. package/dist/dynamic/components/NotificationBadge/package.json +1 -1
  52. package/dist/dynamic/components/NotificationDrawer/package.json +1 -1
  53. package/dist/dynamic/components/NumberInput/package.json +1 -1
  54. package/dist/dynamic/components/OverflowMenu/package.json +1 -1
  55. package/dist/dynamic/components/Page/package.json +1 -1
  56. package/dist/dynamic/components/Pagination/package.json +1 -1
  57. package/dist/dynamic/components/Panel/package.json +1 -1
  58. package/dist/dynamic/components/Popover/package.json +1 -1
  59. package/dist/dynamic/components/Progress/package.json +1 -1
  60. package/dist/dynamic/components/ProgressStepper/package.json +1 -1
  61. package/dist/dynamic/components/Radio/package.json +1 -1
  62. package/dist/dynamic/components/SearchInput/package.json +1 -1
  63. package/dist/dynamic/components/Select/package.json +1 -1
  64. package/dist/dynamic/components/Sidebar/package.json +1 -1
  65. package/dist/dynamic/components/SimpleList/package.json +1 -1
  66. package/dist/dynamic/components/Skeleton/package.json +1 -1
  67. package/dist/dynamic/components/SkipToContent/package.json +1 -1
  68. package/dist/dynamic/components/Slider/package.json +1 -1
  69. package/dist/dynamic/components/Spinner/package.json +1 -1
  70. package/dist/dynamic/components/Switch/package.json +1 -1
  71. package/dist/dynamic/components/Tabs/package.json +1 -1
  72. package/dist/dynamic/components/TextArea/package.json +1 -1
  73. package/dist/dynamic/components/TextInput/package.json +1 -1
  74. package/dist/dynamic/components/TextInputGroup/package.json +1 -1
  75. package/dist/dynamic/components/TimePicker/package.json +1 -1
  76. package/dist/dynamic/components/Timestamp/package.json +1 -1
  77. package/dist/dynamic/components/Title/package.json +1 -1
  78. package/dist/dynamic/components/ToggleGroup/package.json +1 -1
  79. package/dist/dynamic/components/Toolbar/package.json +1 -1
  80. package/dist/dynamic/components/Tooltip/package.json +1 -1
  81. package/dist/dynamic/components/TreeView/package.json +1 -1
  82. package/dist/dynamic/components/Truncate/package.json +1 -1
  83. package/dist/dynamic/components/Wizard/hooks/package.json +1 -1
  84. package/dist/dynamic/components/Wizard/package.json +1 -1
  85. package/dist/dynamic/deprecated/components/Chip/package.json +1 -1
  86. package/dist/dynamic/deprecated/components/DragDrop/package.json +1 -1
  87. package/dist/dynamic/deprecated/components/DualListSelector/package.json +1 -1
  88. package/dist/dynamic/deprecated/components/Modal/package.json +1 -1
  89. package/dist/dynamic/deprecated/components/Tile/package.json +1 -1
  90. package/dist/dynamic/deprecated/components/Wizard/package.json +1 -1
  91. package/dist/dynamic/deprecated/components/package.json +1 -1
  92. package/dist/dynamic/helpers/AnimationsProvider/AnimationsProvider/package.json +1 -1
  93. package/dist/dynamic/helpers/AnimationsProvider/package.json +1 -1
  94. package/dist/dynamic/helpers/FocusTrap/FocusTrap/package.json +1 -1
  95. package/dist/dynamic/helpers/GenerateId/GenerateId/package.json +1 -1
  96. package/dist/dynamic/helpers/KeyboardHandler/package.json +1 -1
  97. package/dist/dynamic/helpers/OUIA/ouia/package.json +1 -1
  98. package/dist/dynamic/helpers/Popper/Popper/package.json +1 -1
  99. package/dist/dynamic/helpers/SSRSafeIds/SSRSafeIds/package.json +1 -1
  100. package/dist/dynamic/helpers/constants/package.json +1 -1
  101. package/dist/dynamic/helpers/datetimeUtils/package.json +1 -1
  102. package/dist/dynamic/helpers/fileUtils/package.json +1 -1
  103. package/dist/dynamic/helpers/htmlConstants/package.json +1 -1
  104. package/dist/dynamic/helpers/inert/package.json +1 -1
  105. package/dist/dynamic/helpers/package.json +1 -1
  106. package/dist/dynamic/helpers/resizeObserver/package.json +1 -1
  107. package/dist/dynamic/helpers/typeUtils/package.json +1 -1
  108. package/dist/dynamic/helpers/useInterval/package.json +1 -1
  109. package/dist/dynamic/helpers/useIsomorphicLayout/package.json +1 -1
  110. package/dist/dynamic/helpers/useSSRSafeId/package.json +1 -1
  111. package/dist/dynamic/helpers/useUnmountEffect/package.json +1 -1
  112. package/dist/dynamic/helpers/util/package.json +1 -1
  113. package/dist/dynamic/layouts/Bullseye/package.json +1 -1
  114. package/dist/dynamic/layouts/Flex/package.json +1 -1
  115. package/dist/dynamic/layouts/Gallery/package.json +1 -1
  116. package/dist/dynamic/layouts/Grid/package.json +1 -1
  117. package/dist/dynamic/layouts/Level/package.json +1 -1
  118. package/dist/dynamic/layouts/Split/package.json +1 -1
  119. package/dist/dynamic/layouts/Stack/package.json +1 -1
  120. package/dist/dynamic/styles/package.json +1 -1
  121. package/dist/esm/components/Modal/Modal.d.ts +2 -0
  122. package/dist/esm/components/Modal/Modal.d.ts.map +1 -1
  123. package/dist/esm/components/Modal/Modal.js +39 -7
  124. package/dist/esm/components/Modal/Modal.js.map +1 -1
  125. package/dist/js/components/Modal/Modal.d.ts +2 -0
  126. package/dist/js/components/Modal/Modal.d.ts.map +1 -1
  127. package/dist/js/components/Modal/Modal.js +39 -7
  128. package/dist/js/components/Modal/Modal.js.map +1 -1
  129. package/dist/umd/assets/{output-eXFa6Wof.css → output-CB485rDk.css} +21006 -21006
  130. package/dist/umd/react-core.min.js +1 -1
  131. package/helpers/package.json +1 -1
  132. package/layouts/package.json +1 -1
  133. package/next/package.json +1 -1
  134. package/package.json +2 -2
  135. package/src/components/Modal/Modal.tsx +42 -7
  136. package/src/components/Modal/__tests__/Modal.test.tsx +152 -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.6.1-prerelease.2","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.6.1-prerelease.3","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.6.1-prerelease.2","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.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.2","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.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",
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": "f20f0738c88cf45f51cdae7239b92ee6aaa26510"
66
+ "gitHead": "1fe30b1dc78a10ff784587cf7ce03d96125fbfb7"
67
67
  }
@@ -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
- const bodyChildren = target.children;
113
- for (const child of Array.from(bodyChildren)) {
114
- const isPopperElement = child.hasAttribute('data-popper-placement');
115
- if (child.id !== this.backdropId && !isPopperElement) {
116
- hide ? child.setAttribute('aria-hidden', '' + hide) : child.removeAttribute('aria-hidden');
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
  });