@patternfly/chatbot 6.8.0-prerelease.3 → 6.8.0-prerelease.5

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 (112) hide show
  1. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -5
  2. package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +3 -3
  3. package/dist/cjs/ChatbotHeader/ChatbotHeaderCloseButton.js +1 -1
  4. package/dist/cjs/ChatbotHeader/ChatbotHeaderMenu.js +2 -5
  5. package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +2 -2
  6. package/dist/cjs/FileDropZone/FileDropZone.js +1 -1
  7. package/dist/cjs/MarkdownContent/MarkdownContent.js +15 -1
  8. package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
  9. package/dist/cjs/Message/LinkMessage/LinkMessage.js +5 -1
  10. package/dist/cjs/Message/Message.test.js +1 -18
  11. package/dist/cjs/Message/QuickStarts/QuickStartTileDescription.js +2 -5
  12. package/dist/cjs/MessageBar/MessageBar.test.js +1 -1
  13. package/dist/cjs/MessageBox/JumpButton.test.js +2 -2
  14. package/dist/cjs/MessageBox/MessageBox.js +79 -28
  15. package/dist/cjs/MessageBox/MessageBox.test.d.ts +1 -1
  16. package/dist/cjs/MessageBox/MessageBox.test.js +59 -0
  17. package/dist/cjs/ResponseActions/ResponseActionButton.test.js +6 -6
  18. package/dist/cjs/ResponseActions/ResponseActions.test.js +6 -7
  19. package/dist/cjs/SourcesCardBase/SourcesCardBase.js +5 -5
  20. package/dist/cjs/index.d.ts +0 -2
  21. package/dist/cjs/index.js +1 -4
  22. package/dist/css/main.css +0 -13
  23. package/dist/css/main.css.map +1 -1
  24. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
  25. package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +4 -4
  26. package/dist/esm/ChatbotHeader/ChatbotHeaderCloseButton.js +2 -2
  27. package/dist/esm/ChatbotHeader/ChatbotHeaderMenu.js +2 -2
  28. package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +2 -2
  29. package/dist/esm/FileDropZone/FileDropZone.js +2 -2
  30. package/dist/esm/MarkdownContent/MarkdownContent.js +15 -1
  31. package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
  32. package/dist/esm/Message/LinkMessage/LinkMessage.js +6 -2
  33. package/dist/esm/Message/Message.test.js +1 -18
  34. package/dist/esm/Message/QuickStarts/QuickStartTileDescription.js +2 -2
  35. package/dist/esm/MessageBar/MessageBar.test.js +2 -2
  36. package/dist/esm/MessageBox/JumpButton.test.js +2 -2
  37. package/dist/esm/MessageBox/MessageBox.js +80 -29
  38. package/dist/esm/MessageBox/MessageBox.test.d.ts +1 -1
  39. package/dist/esm/MessageBox/MessageBox.test.js +59 -0
  40. package/dist/esm/ResponseActions/ResponseActionButton.test.js +7 -7
  41. package/dist/esm/ResponseActions/ResponseActions.test.js +7 -8
  42. package/dist/esm/SourcesCardBase/SourcesCardBase.js +7 -7
  43. package/dist/esm/index.d.ts +0 -2
  44. package/dist/esm/index.js +0 -2
  45. package/dist/tsconfig.tsbuildinfo +1 -1
  46. package/package.json +2 -2
  47. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithCustomResponseActions.tsx +3 -4
  48. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +3 -3
  49. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +3 -3
  50. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithSources.tsx +3 -3
  51. package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +5 -5
  52. package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +1 -1
  53. package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +7 -31
  54. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +4 -6
  55. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +3 -3
  56. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerResizable.tsx +2 -2
  57. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +2 -2
  58. package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSearchActions.tsx +3 -3
  59. package/patternfly-docs/content/extensions/chatbot/examples/UI/CompactSettings.tsx +10 -5
  60. package/patternfly-docs/content/extensions/chatbot/examples/UI/Settings.tsx +10 -5
  61. package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +1 -15
  62. package/patternfly-docs/content/extensions/chatbot/examples/demos/AttachmentDemos.md +2 -4
  63. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +2 -5
  64. package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +4 -7
  65. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachment.tsx +4 -6
  66. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachmentMenu.tsx +13 -7
  67. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx +4 -7
  68. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotDisplayMode.tsx +10 -8
  69. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotInDrawer.tsx +2 -2
  70. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotScrolling.tsx +4 -8
  71. package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +4 -7
  72. package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedChatbot.tsx +2 -2
  73. package/patternfly-docs/content/extensions/chatbot/examples/demos/EmbeddedComparisonChatbot.tsx +2 -2
  74. package/patternfly-docs/content/extensions/chatbot/examples/demos/WhiteEmbeddedChatbot.tsx +2 -2
  75. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +2 -3
  76. package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +4 -4
  77. package/src/ChatbotHeader/ChatbotHeaderCloseButton.tsx +2 -2
  78. package/src/ChatbotHeader/ChatbotHeaderMenu.tsx +2 -2
  79. package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +2 -3
  80. package/src/FileDropZone/FileDropZone.tsx +2 -2
  81. package/src/MarkdownContent/MarkdownContent.tsx +14 -0
  82. package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +2 -2
  83. package/src/Message/LinkMessage/LinkMessage.tsx +21 -2
  84. package/src/Message/Message.test.tsx +4 -4
  85. package/src/Message/QuickStarts/QuickStartTileDescription.tsx +2 -2
  86. package/src/MessageBar/MessageBar.test.tsx +6 -6
  87. package/src/MessageBox/JumpButton.test.tsx +1 -1
  88. package/src/MessageBox/MessageBox.test.tsx +91 -0
  89. package/src/MessageBox/MessageBox.tsx +137 -40
  90. package/src/ResponseActions/ResponseActionButton.test.tsx +7 -7
  91. package/src/ResponseActions/ResponseActions.test.tsx +7 -8
  92. package/src/SourcesCard/SourcesCard.scss +0 -13
  93. package/src/SourcesCardBase/SourcesCardBase.tsx +7 -32
  94. package/dist/cjs/LoadingMessage/LoadingMessage.d.ts +0 -6
  95. package/dist/cjs/LoadingMessage/LoadingMessage.js +0 -12
  96. package/dist/cjs/LoadingMessage/LoadingMessage.test.d.ts +0 -1
  97. package/dist/cjs/LoadingMessage/LoadingMessage.test.js +0 -20
  98. package/dist/cjs/LoadingMessage/index.d.ts +0 -2
  99. package/dist/cjs/LoadingMessage/index.js +0 -23
  100. package/dist/dynamic/LoadingMessage/package.json +0 -1
  101. package/dist/esm/LoadingMessage/LoadingMessage.d.ts +0 -6
  102. package/dist/esm/LoadingMessage/LoadingMessage.js +0 -5
  103. package/dist/esm/LoadingMessage/LoadingMessage.test.d.ts +0 -1
  104. package/dist/esm/LoadingMessage/LoadingMessage.test.js +0 -15
  105. package/dist/esm/LoadingMessage/index.d.ts +0 -2
  106. package/dist/esm/LoadingMessage/index.js +0 -2
  107. package/patternfly-docs/content/extensions/chatbot/examples/Messages/AttachMenu.tsx +0 -149
  108. package/patternfly-docs/content/extensions/chatbot/examples/Messages/PaperclipIcon.svg +0 -10
  109. package/src/LoadingMessage/LoadingMessage.test.tsx +0 -15
  110. package/src/LoadingMessage/LoadingMessage.tsx +0 -27
  111. package/src/LoadingMessage/__snapshots__/LoadingMessage.test.tsx.snap +0 -64
  112. package/src/LoadingMessage/index.ts +0 -3
@@ -7,7 +7,7 @@ import {
7
7
  useState,
8
8
  useRef,
9
9
  useCallback,
10
- useEffect,
10
+ useLayoutEffect,
11
11
  forwardRef,
12
12
  ForwardedRef,
13
13
  useImperativeHandle,
@@ -18,7 +18,7 @@ import {
18
18
  WheelEventHandler
19
19
  } from 'react';
20
20
  import JumpButton from './JumpButton';
21
- import { ButtonProps, TooltipProps } from '@patternfly/react-core';
21
+ import { ButtonProps, getResizeObserver, TooltipProps } from '@patternfly/react-core';
22
22
 
23
23
  export interface MessageBoxProps extends HTMLProps<HTMLDivElement> {
24
24
  /** Content that can be announced, such as a new message, for screen readers */
@@ -58,6 +58,48 @@ export interface MessageBoxHandle extends HTMLDivElement {
58
58
  isSmartScrollActive: () => boolean;
59
59
  }
60
60
 
61
+ const SCROLL_EDGE_DELTA = 100;
62
+
63
+ const getScrollMetrics = (element: HTMLDivElement) => {
64
+ const { scrollTop, scrollHeight, clientHeight } = element;
65
+ const roundedScrollTop = Math.round(scrollTop);
66
+ const roundedClientHeight = Math.round(clientHeight);
67
+ const roundedScrollHeight = Math.round(scrollHeight);
68
+ const distanceFromBottom = roundedScrollHeight - roundedScrollTop - roundedClientHeight;
69
+
70
+ return {
71
+ roundedScrollTop,
72
+ roundedClientHeight,
73
+ roundedScrollHeight,
74
+ distanceFromBottom,
75
+ atTop: roundedScrollTop === 0,
76
+ nearBottom: distanceFromBottom <= SCROLL_EDGE_DELTA,
77
+ isOverflowing: roundedScrollHeight >= roundedClientHeight
78
+ };
79
+ };
80
+
81
+ const isAtBottomEdge = (
82
+ metrics: ReturnType<typeof getScrollMetrics>,
83
+ enableSmartScroll: boolean,
84
+ autoScroll: boolean,
85
+ pauseAutoScroll: boolean,
86
+ programmaticScroll = false
87
+ ) =>
88
+ metrics.nearBottom ||
89
+ programmaticScroll ||
90
+ (enableSmartScroll && autoScroll && !pauseAutoScroll && metrics.roundedScrollTop > 0);
91
+
92
+ const shouldFollowContentGrowth = (
93
+ metrics: ReturnType<typeof getScrollMetrics>,
94
+ enableSmartScroll: boolean,
95
+ autoScroll: boolean,
96
+ pauseAutoScroll: boolean,
97
+ programmaticScroll: boolean
98
+ ) =>
99
+ programmaticScroll ||
100
+ metrics.nearBottom ||
101
+ (enableSmartScroll && autoScroll && !pauseAutoScroll && metrics.roundedScrollTop > 0);
102
+
61
103
  export const MessageBox = forwardRef(
62
104
  (
63
105
  {
@@ -77,18 +119,23 @@ export const MessageBox = forwardRef(
77
119
  }: MessageBoxProps,
78
120
  ref: ForwardedRef<MessageBoxHandle | null>
79
121
  ) => {
80
- const [atTop, setAtTop] = useState(false);
81
- const [atBottom, setAtBottom] = useState(true);
122
+ const [atTop, setAtTop] = useState(true);
123
+ const [atBottom, setAtBottom] = useState(false);
82
124
  const [isOverflowing, setIsOverflowing] = useState(false);
125
+ const [hasMeasuredScroll, setHasMeasuredScroll] = useState(false);
83
126
  const [autoScroll, setAutoScroll] = useState(true);
127
+ const autoScrollRef = useRef(autoScroll);
84
128
  const lastScrollTop = useRef(0);
85
129
  const animationFrame = useRef<any>(null);
86
130
  const debounceTimeout = useRef<NodeJS.Timeout | null>(null);
87
131
  const pauseAutoScrollRef = useRef(false);
132
+ const programmaticScrollRef = useRef(false);
88
133
  const messageBoxRef = useRef<HTMLDivElement>(null);
89
134
  const scrollQueued = useRef(false);
90
135
  const resetUserScrollIntentTimeout = useRef<NodeJS.Timeout>();
91
136
 
137
+ autoScrollRef.current = autoScroll;
138
+
92
139
  // Configure handlers
93
140
  const handleScroll = useCallback(() => {
94
141
  const element = messageBoxRef.current;
@@ -96,13 +143,8 @@ export const MessageBox = forwardRef(
96
143
  return;
97
144
  }
98
145
 
99
- const { scrollTop, scrollHeight, clientHeight } = element;
100
-
101
- const roundedScrollTop = Math.round(scrollTop);
102
- const roundedClientHeight = Math.round(clientHeight);
103
- const roundedScrollHeight = Math.round(scrollHeight);
104
-
105
- const distanceFromBottom = roundedScrollHeight - roundedScrollTop - roundedClientHeight;
146
+ const metrics = getScrollMetrics(element);
147
+ const { roundedScrollTop, distanceFromBottom } = metrics;
106
148
  const isScrollingDown = roundedScrollTop > lastScrollTop.current;
107
149
 
108
150
  const DELTA_UP = 10;
@@ -112,14 +154,27 @@ export const MessageBox = forwardRef(
112
154
  const delta = isScrollingDown ? DELTA_DOWN : DELTA_UP;
113
155
  const isAtBottom = distanceFromBottom <= delta;
114
156
 
115
- setAtTop(roundedScrollTop === 0);
116
- setAtBottom(roundedScrollTop + roundedClientHeight >= roundedScrollHeight - 1); // rounding means it could be within a pixel of the bottom
157
+ setAtTop(metrics.atTop);
158
+ setAtBottom(
159
+ isAtBottomEdge(
160
+ metrics,
161
+ enableSmartScroll,
162
+ autoScrollRef.current,
163
+ pauseAutoScrollRef.current,
164
+ programmaticScrollRef.current
165
+ )
166
+ );
167
+ setIsOverflowing(metrics.isOverflowing);
168
+
169
+ if (isAtBottom) {
170
+ programmaticScrollRef.current = false;
171
+ }
117
172
 
118
173
  if (!enableSmartScroll || scrollQueued.current) {
119
174
  return;
120
175
  }
121
176
 
122
- if (roundedScrollTop === 0) {
177
+ if (metrics.roundedScrollTop === 0) {
123
178
  pauseAutoScrollRef.current = false;
124
179
  }
125
180
 
@@ -127,7 +182,7 @@ export const MessageBox = forwardRef(
127
182
  clearTimeout(debounceTimeout.current);
128
183
  }
129
184
 
130
- if (!isAtBottom && !pauseAutoScrollRef.current) {
185
+ if (!isAtBottom && !metrics.nearBottom && !pauseAutoScrollRef.current && !programmaticScrollRef.current) {
131
186
  setAutoScroll(false);
132
187
  }
133
188
 
@@ -139,15 +194,7 @@ export const MessageBox = forwardRef(
139
194
  }
140
195
 
141
196
  lastScrollTop.current = roundedScrollTop;
142
- }, [messageBoxRef]);
143
-
144
- const checkOverflow = useCallback(() => {
145
- const element = messageBoxRef.current;
146
- if (element) {
147
- const { scrollHeight, clientHeight } = element;
148
- setIsOverflowing(scrollHeight >= clientHeight);
149
- }
150
- }, [messageBoxRef]);
197
+ }, [enableSmartScroll]);
151
198
 
152
199
  const resumeAutoScroll = useCallback(() => {
153
200
  if (!enableSmartScroll) {
@@ -213,6 +260,7 @@ export const MessageBox = forwardRef(
213
260
  }
214
261
 
215
262
  scrollQueued.current = true;
263
+ programmaticScrollRef.current = true;
216
264
 
217
265
  if (animationFrame.current) {
218
266
  cancelAnimationFrame(animationFrame.current);
@@ -222,30 +270,78 @@ export const MessageBox = forwardRef(
222
270
  element.scrollTo({ top: element.scrollHeight, behavior });
223
271
  resumeAutoScroll();
224
272
  scrollQueued.current = false;
273
+ if (behavior === 'auto') {
274
+ programmaticScrollRef.current = false;
275
+ lastScrollTop.current = element.scrollTop;
276
+ }
225
277
  });
226
278
  onScrollToBottomClick && onScrollToBottomClick();
227
279
  },
228
280
  [messageBoxRef, enableSmartScroll]
229
281
  );
230
282
 
231
- // Detect scroll position
232
- useEffect(() => {
283
+ const measureJumpButtonState = useCallback(() => {
284
+ const element = messageBoxRef.current;
285
+ if (!element) {
286
+ return;
287
+ }
288
+
289
+ const metrics = getScrollMetrics(element);
290
+
291
+ setAtTop(metrics.atTop);
292
+ setAtBottom(
293
+ isAtBottomEdge(
294
+ metrics,
295
+ enableSmartScroll,
296
+ autoScrollRef.current,
297
+ pauseAutoScrollRef.current,
298
+ programmaticScrollRef.current
299
+ )
300
+ );
301
+ setIsOverflowing(metrics.isOverflowing);
302
+ setHasMeasuredScroll(true);
303
+ }, [enableSmartScroll]);
304
+
305
+ const handleContentResize = useCallback(() => {
306
+ const element = messageBoxRef.current;
307
+ if (element) {
308
+ const metrics = getScrollMetrics(element);
309
+ const shouldFollow = shouldFollowContentGrowth(
310
+ metrics,
311
+ enableSmartScroll,
312
+ autoScrollRef.current,
313
+ pauseAutoScrollRef.current,
314
+ programmaticScrollRef.current
315
+ );
316
+
317
+ if (shouldFollow && !scrollQueued.current) {
318
+ programmaticScrollRef.current = true;
319
+ element.scrollTop = element.scrollHeight - element.clientHeight;
320
+ lastScrollTop.current = element.scrollTop;
321
+ }
322
+ }
323
+
324
+ measureJumpButtonState();
325
+ }, [enableSmartScroll, measureJumpButtonState]);
326
+
327
+ // Detect scroll position before paint to avoid jump button flash on mount
328
+ useLayoutEffect(() => {
233
329
  const element = messageBoxRef.current;
234
330
  if (!element) {
235
331
  return;
236
332
  }
237
333
 
238
- // Listen for scroll events
334
+ measureJumpButtonState();
335
+
239
336
  element.addEventListener('scroll', handleScroll);
240
337
 
241
- // Check initial position and overflow
242
- handleScroll();
243
- checkOverflow();
338
+ const resizeObserver = getResizeObserver(element, handleContentResize);
244
339
 
245
340
  return () => {
246
341
  element.removeEventListener('scroll', handleScroll);
342
+ resizeObserver();
247
343
  };
248
- }, [checkOverflow, handleScroll, messageBoxRef]);
344
+ }, [handleContentResize, handleScroll, measureJumpButtonState]);
249
345
 
250
346
  useImperativeHandle(ref, (): MessageBoxHandle => {
251
347
  const node = messageBoxRef.current! as MessageBoxHandle;
@@ -273,6 +369,7 @@ export const MessageBox = forwardRef(
273
369
 
274
370
  if (!isScrollingDown) {
275
371
  pauseAutoScrollRef.current = true;
372
+ programmaticScrollRef.current = false;
276
373
  clearTimeout(resetUserScrollIntentTimeout.current);
277
374
  return;
278
375
  }
@@ -280,7 +377,7 @@ export const MessageBox = forwardRef(
280
377
  const { scrollTop, scrollHeight, clientHeight } = container;
281
378
  const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
282
379
 
283
- if (distanceFromBottom < 100) {
380
+ if (distanceFromBottom < SCROLL_EDGE_DELTA) {
284
381
  pauseAutoScrollRef.current = false;
285
382
  setAutoScroll(true);
286
383
  }
@@ -320,11 +417,18 @@ export const MessageBox = forwardRef(
320
417
  <>
321
418
  <JumpButton
322
419
  position="top"
323
- isHidden={isOverflowing && atTop}
420
+ isHidden={!hasMeasuredScroll || (isOverflowing && atTop)}
324
421
  onClick={scrollToTop}
325
422
  jumpButtonProps={jumpButtonTopProps}
326
423
  jumpButtonTooltipProps={jumpButtonTopTooltipProps}
327
424
  />
425
+ <JumpButton
426
+ position="bottom"
427
+ isHidden={!hasMeasuredScroll || (isOverflowing && atBottom)}
428
+ onClick={() => scrollToBottom({ resumeSmartScroll: true })}
429
+ jumpButtonProps={jumpButtonBottomProps}
430
+ jumpButtonTooltipProps={jumpButtonBottomTooltipProps}
431
+ />
328
432
  <div
329
433
  role="region"
330
434
  tabIndex={0}
@@ -339,13 +443,6 @@ export const MessageBox = forwardRef(
339
443
  {announcement}
340
444
  </div>
341
445
  </div>
342
- <JumpButton
343
- position="bottom"
344
- isHidden={isOverflowing && atBottom}
345
- onClick={() => scrollToBottom({ resumeSmartScroll: true })}
346
- jumpButtonProps={jumpButtonBottomProps}
347
- jumpButtonTooltipProps={jumpButtonBottomTooltipProps}
348
- />
349
446
  </>
350
447
  );
351
448
  }
@@ -1,23 +1,23 @@
1
1
  import { render, screen } from '@testing-library/react';
2
2
  import '@testing-library/jest-dom';
3
3
  import userEvent from '@testing-library/user-event';
4
- import { DownloadIcon } from '@patternfly/react-icons';
4
+ import { RhUiExportIcon } from '@patternfly/react-icons';
5
5
  import ResponseActionButton from './ResponseActionButton';
6
6
 
7
7
  describe('ResponseActionButton', () => {
8
8
  it('renders aria-label correctly if not clicked', () => {
9
- render(<ResponseActionButton icon={<DownloadIcon />} ariaLabel="Download" clickedAriaLabel="Downloaded" />);
9
+ render(<ResponseActionButton icon={<RhUiExportIcon />} ariaLabel="Download" clickedAriaLabel="Downloaded" />);
10
10
  expect(screen.getByRole('button', { name: 'Download' })).toBeTruthy();
11
11
  });
12
12
  it('renders aria-label correctly if clicked', () => {
13
13
  render(
14
- <ResponseActionButton icon={<DownloadIcon />} ariaLabel="Download" clickedAriaLabel="Downloaded" isClicked />
14
+ <ResponseActionButton icon={<RhUiExportIcon />} ariaLabel="Download" clickedAriaLabel="Downloaded" isClicked />
15
15
  );
16
16
  expect(screen.getByRole('button', { name: 'Downloaded' })).toBeTruthy();
17
17
  });
18
18
  it('renders tooltip correctly if not clicked', async () => {
19
19
  render(
20
- <ResponseActionButton icon={<DownloadIcon />} tooltipContent="Download" clickedTooltipContent="Downloaded" />
20
+ <ResponseActionButton icon={<RhUiExportIcon />} tooltipContent="Download" clickedTooltipContent="Downloaded" />
21
21
  );
22
22
  expect(screen.getByRole('button', { name: 'Download' })).toBeTruthy();
23
23
  // clicking here just triggers the tooltip; in this button, the logic is divorced from whether it is actually clicked
@@ -27,7 +27,7 @@ describe('ResponseActionButton', () => {
27
27
  it('renders tooltip correctly if clicked', async () => {
28
28
  render(
29
29
  <ResponseActionButton
30
- icon={<DownloadIcon />}
30
+ icon={<RhUiExportIcon />}
31
31
  tooltipContent="Download"
32
32
  clickedTooltipContent="Downloaded"
33
33
  isClicked
@@ -39,13 +39,13 @@ describe('ResponseActionButton', () => {
39
39
  expect(screen.getByRole('tooltip', { name: 'Downloaded' })).toBeTruthy();
40
40
  });
41
41
  it('if clicked variant for tooltip is not supplied, it uses the default', async () => {
42
- render(<ResponseActionButton icon={<DownloadIcon />} tooltipContent="Download" isClicked />);
42
+ render(<ResponseActionButton icon={<RhUiExportIcon />} tooltipContent="Download" isClicked />);
43
43
  // clicking here just triggers the tooltip; in this button, the logic is divorced from whether it is actually clicked
44
44
  await userEvent.click(screen.getByRole('button', { name: 'Download' }));
45
45
  expect(screen.getByRole('button', { name: 'Download' })).toBeTruthy();
46
46
  });
47
47
  it('if clicked variant for aria label is not supplied, it uses the default', async () => {
48
- render(<ResponseActionButton icon={<DownloadIcon />} ariaLabel="Download" isClicked />);
48
+ render(<ResponseActionButton icon={<RhUiExportIcon />} ariaLabel="Download" isClicked />);
49
49
  expect(screen.getByRole('button', { name: 'Download' })).toBeTruthy();
50
50
  });
51
51
  });
@@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react';
2
2
  import '@testing-library/jest-dom';
3
3
  import ResponseActions, { ActionProps } from './ResponseActions';
4
4
  import userEvent from '@testing-library/user-event';
5
- import { DownloadIcon, InfoCircleIcon, RedoIcon } from '@patternfly/react-icons';
5
+ import { RhUiExportIcon, RhMicronsInformationFillIcon, RhUiRedoIcon } from '@patternfly/react-icons';
6
6
  import Message from '../Message';
7
7
 
8
8
  // Mock the icon components
@@ -19,9 +19,8 @@ jest.mock('@patternfly/react-icons', () => ({
19
19
  RhUiVolumeUpFillIcon: () => <div>RhUiVolumeUpFillIcon</div>,
20
20
  RhUiExportIcon: () => <div>RhUiExportIcon</div>,
21
21
  RhUiExportFillIcon: () => <div>RhUiExportFillIcon</div>,
22
- DownloadIcon: () => <div>DownloadIcon</div>,
23
- InfoCircleIcon: () => <div>InfoCircleIcon</div>,
24
- RedoIcon: () => <div>RedoIcon</div>
22
+ RhMicronsInformationFillIcon: () => <div>RhMicronsInformationFillIcon</div>,
23
+ RhUiRedoIcon: () => <div>RhUiRedoIcon</div>
25
24
  }));
26
25
 
27
26
  const ALL_ACTIONS = [
@@ -41,7 +40,7 @@ const CUSTOM_ACTIONS = [
41
40
  onClick: jest.fn(),
42
41
  tooltipContent: 'Regenerate',
43
42
  clickedTooltipContent: 'Regenerated',
44
- icon: <RedoIcon />
43
+ icon: <RhUiRedoIcon />
45
44
  },
46
45
  download: {
47
46
  ariaLabel: 'Download',
@@ -49,13 +48,13 @@ const CUSTOM_ACTIONS = [
49
48
  onClick: jest.fn(),
50
49
  tooltipContent: 'Download',
51
50
  clickedTooltipContent: 'Downloaded',
52
- icon: <DownloadIcon />
51
+ icon: <RhUiExportIcon />
53
52
  },
54
53
  info: {
55
54
  ariaLabel: 'Info',
56
55
  onClick: jest.fn(),
57
56
  tooltipContent: 'Info',
58
- icon: <InfoCircleIcon />
57
+ icon: <RhMicronsInformationFillIcon />
59
58
  }
60
59
  }
61
60
  ];
@@ -428,7 +427,7 @@ describe('ResponseActions', () => {
428
427
  onClick: jest.fn(),
429
428
  ariaLabel: 'Custom',
430
429
  tooltipContent: 'Custom action',
431
- icon: <DownloadIcon />
430
+ icon: <RhUiExportIcon />
432
431
  }
433
432
  };
434
433
  render(<ResponseActions actions={actions} persistActionSelection />);
@@ -79,25 +79,12 @@
79
79
  justify-content: space-between;
80
80
  flex: 1;
81
81
 
82
- .pf-v6-c-button {
83
- border-radius: var(--pf-t--global--border--radius--pill);
84
- padding: var(--pf-t--global--spacer--sm);
85
- width: 2.31rem;
86
- height: 2.31rem;
87
- display: flex;
88
- align-items: center;
89
- justify-content: center;
90
- }
91
82
  button:disabled,
92
83
  button[disabled] {
93
84
  .pf-v6-c-icon__content {
94
85
  color: var(--pf-t--global--icon--color--disabled);
95
86
  }
96
87
  }
97
- .pf-v6-c-button__text {
98
- display: flex;
99
- align-items: center;
100
- }
101
88
  // Interactive states
102
89
  .pf-v6-c-button:hover,
103
90
  .pf-v6-c-button:focus {
@@ -23,11 +23,10 @@ import {
23
23
  CardTitleProps,
24
24
  ExpandableSection,
25
25
  ExpandableSectionVariant,
26
- Icon,
27
26
  Truncate,
28
27
  TruncateProps
29
28
  } from '@patternfly/react-core';
30
- import { ExternalLinkSquareAltIcon } from '@patternfly/react-icons';
29
+ import { RhMicronsCaretLeftIcon, RhMicronsCaretRightIcon, RhMicronsExternalLinkIcon } from '@patternfly/react-icons';
31
30
 
32
31
  export interface SourcesCardBaseProps extends CardProps {
33
32
  /** Additional classes for the pagination navigation container. */
@@ -139,7 +138,7 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
139
138
  component="a"
140
139
  variant={ButtonVariant.link}
141
140
  href={source.link}
142
- icon={source.isExternal ? <ExternalLinkSquareAltIcon /> : undefined}
141
+ icon={source.isExternal ? <RhMicronsExternalLinkIcon /> : undefined}
143
142
  iconPosition="end"
144
143
  isInline
145
144
  rel={source.isExternal ? 'noreferrer' : undefined}
@@ -202,7 +201,7 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
202
201
  component="a"
203
202
  variant={ButtonVariant.link}
204
203
  href={sources[page - 1].link}
205
- icon={sources[page - 1].isExternal ? <ExternalLinkSquareAltIcon /> : undefined}
204
+ icon={sources[page - 1].isExternal ? <RhMicronsExternalLinkIcon /> : undefined}
206
205
  iconPosition="end"
207
206
  isInline
208
207
  rel={sources[page - 1].isExternal ? 'noreferrer' : undefined}
@@ -254,6 +253,7 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
254
253
  >
255
254
  <Button
256
255
  variant={ButtonVariant.plain}
256
+ isCircle
257
257
  isDisabled={isDisabled || page === 1}
258
258
  data-action="previous"
259
259
  onClick={(event) => {
@@ -263,26 +263,14 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
263
263
  }}
264
264
  aria-label={toPreviousPageAriaLabel}
265
265
  >
266
- <Icon iconSize="lg">
267
- {/* these are inline because the viewBox that works in a round icon is different than the PatternFly default */}
268
- <svg
269
- className="pf-v6-svg"
270
- viewBox="0 0 280 500"
271
- fill="currentColor"
272
- aria-hidden="true"
273
- role="img"
274
- width="1em"
275
- height="1em"
276
- >
277
- <path d="M31.7 239l136-136c9.4-9.4 24.6-9.4 33.9 0l22.6 22.6c9.4 9.4 9.4 24.6 0 33.9L127.9 256l96.4 96.4c9.4 9.4 9.4 24.6 0 33.9L201.7 409c-9.4 9.4-24.6 9.4-33.9 0l-136-136c-9.5-9.4-9.5-24.6-.1-34z"></path>
278
- </svg>
279
- </Icon>
266
+ <RhMicronsCaretLeftIcon />
280
267
  </Button>
281
268
  <span aria-hidden="true">
282
269
  {page}/{sources.length}
283
270
  </span>
284
271
  <Button
285
272
  variant={ButtonVariant.plain}
273
+ isCircle
286
274
  isDisabled={isDisabled || page === sources.length}
287
275
  aria-label={toNextPageAriaLabel}
288
276
  data-action="next"
@@ -292,20 +280,7 @@ const SourcesCardBase: FunctionComponent<SourcesCardBaseProps> = ({
292
280
  handleNewPage(event, newPage);
293
281
  }}
294
282
  >
295
- <Icon isInline iconSize="lg">
296
- {/* these are inline because the viewBox that works in a round icon is different than the PatternFly default */}
297
- <svg
298
- className="pf-v6-svg"
299
- viewBox="0 0 180 500"
300
- fill="currentColor"
301
- aria-hidden="true"
302
- role="img"
303
- width="1em"
304
- height="1em"
305
- >
306
- <path d="M224.3 273l-136 136c-9.4 9.4-24.6 9.4-33.9 0l-22.6-22.6c-9.4-9.4-9.4-24.6 0-33.9l96.4-96.4-96.4-96.4c-9.4-9.4-9.4-24.6 0-33.9L54.3 103c9.4-9.4 24.6-9.4 33.9 0l136 136c9.5 9.4 9.5 24.6.1 34z"></path>
307
- </svg>
308
- </Icon>
283
+ <RhMicronsCaretRightIcon />
309
284
  </Button>
310
285
  </nav>
311
286
  </div>
@@ -1,6 +0,0 @@
1
- import type { FunctionComponent } from 'react';
2
- export interface LoadingMessageProps {
3
- icon?: React.ComponentType;
4
- }
5
- export declare const LoadingMessage: FunctionComponent<LoadingMessageProps>;
6
- export default LoadingMessage;
@@ -1,12 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.LoadingMessage = void 0;
7
- const jsx_runtime_1 = require("react/jsx-runtime");
8
- const react_core_1 = require("@patternfly/react-core");
9
- const robot_icon_1 = __importDefault(require("@patternfly/react-icons/dist/js/icons/robot-icon"));
10
- const LoadingMessage = ({ icon: IconComponent = robot_icon_1.default }) => ((0, jsx_runtime_1.jsxs)(react_core_1.Split, { children: [(0, jsx_runtime_1.jsx)(react_core_1.SplitItem, { children: (0, jsx_runtime_1.jsx)(react_core_1.Icon, { size: "lg", className: "pf-v6-u-mr-sm pf-v6-u-pt-md", children: (0, jsx_runtime_1.jsx)(IconComponent, {}) }) }), (0, jsx_runtime_1.jsx)(react_core_1.SplitItem, { className: "pf-u-background-color-200", children: (0, jsx_runtime_1.jsxs)("div", { className: "pf-v6-u-display-flex pf-u-align-items-center", children: [(0, jsx_runtime_1.jsx)("div", { children: "." }), (0, jsx_runtime_1.jsx)("div", { children: "." }), (0, jsx_runtime_1.jsx)("div", { children: "." })] }) })] }));
11
- exports.LoadingMessage = LoadingMessage;
12
- exports.default = exports.LoadingMessage;
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';
@@ -1,20 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsx_runtime_1 = require("react/jsx-runtime");
7
- const react_1 = require("@testing-library/react");
8
- require("@testing-library/jest-dom");
9
- const LoadingMessage_1 = __importDefault(require("./LoadingMessage"));
10
- describe('LoadingMessage', () => {
11
- it('should render loading message', () => {
12
- const { container } = (0, react_1.render)((0, jsx_runtime_1.jsx)(LoadingMessage_1.default, {}));
13
- expect(container).toMatchSnapshot();
14
- });
15
- it('should render loading message correctly', () => {
16
- (0, react_1.render)((0, jsx_runtime_1.jsx)(LoadingMessage_1.default, {}));
17
- expect(react_1.screen.getAllByText('.')).toBeTruthy();
18
- expect(react_1.screen.getAllByText('.')).toHaveLength(3);
19
- });
20
- });
@@ -1,2 +0,0 @@
1
- export { default } from './LoadingMessage';
2
- export * from './LoadingMessage';
@@ -1,23 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- var __importDefault = (this && this.__importDefault) || function (mod) {
17
- return (mod && mod.__esModule) ? mod : { "default": mod };
18
- };
19
- Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.default = void 0;
21
- var LoadingMessage_1 = require("./LoadingMessage");
22
- Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(LoadingMessage_1).default; } });
23
- __exportStar(require("./LoadingMessage"), exports);
@@ -1 +0,0 @@
1
- {"main":"../../cjs/LoadingMessage/index.js","module":"../../esm/LoadingMessage/index.js","typings":"../../esm/LoadingMessage/index.d.ts"}
@@ -1,6 +0,0 @@
1
- import type { FunctionComponent } from 'react';
2
- export interface LoadingMessageProps {
3
- icon?: React.ComponentType;
4
- }
5
- export declare const LoadingMessage: FunctionComponent<LoadingMessageProps>;
6
- export default LoadingMessage;
@@ -1,5 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Icon, Split, SplitItem } from '@patternfly/react-core';
3
- import RobotIcon from '@patternfly/react-icons/dist/js/icons/robot-icon';
4
- export const LoadingMessage = ({ icon: IconComponent = RobotIcon }) => (_jsxs(Split, { children: [_jsx(SplitItem, { children: _jsx(Icon, { size: "lg", className: "pf-v6-u-mr-sm pf-v6-u-pt-md", children: _jsx(IconComponent, {}) }) }), _jsx(SplitItem, { className: "pf-u-background-color-200", children: _jsxs("div", { className: "pf-v6-u-display-flex pf-u-align-items-center", children: [_jsx("div", { children: "." }), _jsx("div", { children: "." }), _jsx("div", { children: "." })] }) })] }));
5
- export default LoadingMessage;
@@ -1 +0,0 @@
1
- import '@testing-library/jest-dom';
@@ -1,15 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { render, screen } from '@testing-library/react';
3
- import '@testing-library/jest-dom';
4
- import LoadingMessage from './LoadingMessage';
5
- describe('LoadingMessage', () => {
6
- it('should render loading message', () => {
7
- const { container } = render(_jsx(LoadingMessage, {}));
8
- expect(container).toMatchSnapshot();
9
- });
10
- it('should render loading message correctly', () => {
11
- render(_jsx(LoadingMessage, {}));
12
- expect(screen.getAllByText('.')).toBeTruthy();
13
- expect(screen.getAllByText('.')).toHaveLength(3);
14
- });
15
- });
@@ -1,2 +0,0 @@
1
- export { default } from './LoadingMessage';
2
- export * from './LoadingMessage';
@@ -1,2 +0,0 @@
1
- export { default } from './LoadingMessage';
2
- export * from './LoadingMessage';