@patternfly/chatbot 6.8.0-prerelease.2 → 6.8.0-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/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -5
- package/dist/cjs/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +3 -3
- package/dist/cjs/ChatbotHeader/ChatbotHeaderCloseButton.js +1 -1
- package/dist/cjs/ChatbotHeader/ChatbotHeaderNewChatButton.js +2 -2
- package/dist/cjs/MarkdownContent/MarkdownContent.js +15 -1
- package/dist/cjs/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
- package/dist/cjs/Message/LinkMessage/LinkMessage.js +5 -1
- package/dist/cjs/Message/Message.test.js +3 -1
- package/dist/cjs/Message/QuickStarts/QuickStartTileDescription.js +2 -5
- package/dist/cjs/MessageBox/JumpButton.test.js +2 -2
- package/dist/cjs/MessageBox/MessageBox.js +79 -28
- package/dist/cjs/MessageBox/MessageBox.test.d.ts +1 -1
- package/dist/cjs/MessageBox/MessageBox.test.js +59 -0
- package/dist/cjs/ResponseActions/ResponseActionButton.test.js +6 -6
- package/dist/cjs/ResponseActions/ResponseActions.test.js +6 -7
- package/dist/cjs/index.d.ts +0 -2
- package/dist/cjs/index.js +1 -4
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.js +2 -2
- package/dist/esm/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.js +4 -4
- package/dist/esm/ChatbotHeader/ChatbotHeaderCloseButton.js +2 -2
- package/dist/esm/ChatbotHeader/ChatbotHeaderNewChatButton.js +2 -2
- package/dist/esm/MarkdownContent/MarkdownContent.js +15 -1
- package/dist/esm/Message/CodeBlockMessage/CodeBlockMessage.js +2 -2
- package/dist/esm/Message/LinkMessage/LinkMessage.js +6 -2
- package/dist/esm/Message/Message.test.js +3 -1
- package/dist/esm/Message/QuickStarts/QuickStartTileDescription.js +2 -2
- package/dist/esm/MessageBox/JumpButton.test.js +2 -2
- package/dist/esm/MessageBox/MessageBox.js +80 -29
- package/dist/esm/MessageBox/MessageBox.test.d.ts +1 -1
- package/dist/esm/MessageBox/MessageBox.test.js +59 -0
- package/dist/esm/ResponseActions/ResponseActionButton.test.js +7 -7
- package/dist/esm/ResponseActions/ResponseActions.test.js +7 -8
- package/dist/esm/index.d.ts +0 -2
- package/dist/esm/index.js +0 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/patternfly-a11y.config.js +2 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithCustomResponseActions.tsx +3 -4
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithMarkdownToolResponse.tsx +3 -3
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithQuickResponses.tsx +3 -3
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/MessageWithToolResponse.tsx +5 -5
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/Messages.md +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/Messages/UserMessageWithExtraContent.tsx +7 -31
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderBasic.tsx +4 -6
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawer.tsx +3 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerResizable.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithPin.tsx +2 -2
- package/patternfly-docs/content/extensions/chatbot/examples/UI/ChatbotHeaderDrawerWithSearchActions.tsx +3 -3
- package/patternfly-docs/content/extensions/chatbot/examples/UI/CompactSettings.tsx +10 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/Settings.tsx +10 -5
- package/patternfly-docs/content/extensions/chatbot/examples/UI/UI.md +1 -15
- package/patternfly-docs/content/extensions/chatbot/examples/demos/AttachmentDemos.md +1 -3
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.md +2 -5
- package/patternfly-docs/content/extensions/chatbot/examples/demos/Chatbot.tsx +4 -7
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotAttachment.tsx +4 -6
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotCompact.tsx +4 -7
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotDisplayMode.tsx +4 -7
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotScrolling.tsx +4 -8
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotTranscripts.tsx +4 -7
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryDropdown.tsx +2 -3
- package/src/ChatbotConversationHistoryNav/ChatbotConversationHistoryNav.tsx +4 -4
- package/src/ChatbotHeader/ChatbotHeaderCloseButton.tsx +2 -2
- package/src/ChatbotHeader/ChatbotHeaderNewChatButton.tsx +2 -3
- package/src/MarkdownContent/MarkdownContent.tsx +14 -0
- package/src/Message/CodeBlockMessage/CodeBlockMessage.tsx +2 -2
- package/src/Message/LinkMessage/LinkMessage.tsx +21 -2
- package/src/Message/Message.test.tsx +3 -1
- package/src/Message/QuickStarts/QuickStartTileDescription.tsx +2 -2
- package/src/MessageBox/JumpButton.test.tsx +1 -1
- package/src/MessageBox/MessageBox.test.tsx +91 -0
- package/src/MessageBox/MessageBox.tsx +137 -40
- package/src/ResponseActions/ResponseActionButton.test.tsx +7 -7
- package/src/ResponseActions/ResponseActions.test.tsx +7 -8
- package/dist/cjs/LoadingMessage/LoadingMessage.d.ts +0 -6
- package/dist/cjs/LoadingMessage/LoadingMessage.js +0 -12
- package/dist/cjs/LoadingMessage/LoadingMessage.test.d.ts +0 -1
- package/dist/cjs/LoadingMessage/LoadingMessage.test.js +0 -20
- package/dist/cjs/LoadingMessage/index.d.ts +0 -2
- package/dist/cjs/LoadingMessage/index.js +0 -23
- package/dist/dynamic/LoadingMessage/package.json +0 -1
- package/dist/esm/LoadingMessage/LoadingMessage.d.ts +0 -6
- package/dist/esm/LoadingMessage/LoadingMessage.js +0 -5
- package/dist/esm/LoadingMessage/LoadingMessage.test.d.ts +0 -1
- package/dist/esm/LoadingMessage/LoadingMessage.test.js +0 -15
- package/dist/esm/LoadingMessage/index.d.ts +0 -2
- package/dist/esm/LoadingMessage/index.js +0 -2
- package/src/LoadingMessage/LoadingMessage.test.tsx +0 -15
- package/src/LoadingMessage/LoadingMessage.tsx +0 -27
- package/src/LoadingMessage/__snapshots__/LoadingMessage.test.tsx.snap +0 -64
- package/src/LoadingMessage/index.ts +0 -3
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
useState,
|
|
8
8
|
useRef,
|
|
9
9
|
useCallback,
|
|
10
|
-
|
|
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(
|
|
81
|
-
const [atBottom, setAtBottom] = useState(
|
|
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
|
|
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(
|
|
116
|
-
setAtBottom(
|
|
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
|
-
}, [
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
334
|
+
measureJumpButtonState();
|
|
335
|
+
|
|
239
336
|
element.addEventListener('scroll', handleScroll);
|
|
240
337
|
|
|
241
|
-
|
|
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
|
-
}, [
|
|
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 <
|
|
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 {
|
|
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={<
|
|
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={<
|
|
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={<
|
|
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={<
|
|
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={<
|
|
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={<
|
|
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 {
|
|
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
|
-
|
|
23
|
-
|
|
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: <
|
|
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: <
|
|
51
|
+
icon: <RhUiExportIcon />
|
|
53
52
|
},
|
|
54
53
|
info: {
|
|
55
54
|
ariaLabel: 'Info',
|
|
56
55
|
onClick: jest.fn(),
|
|
57
56
|
tooltipContent: 'Info',
|
|
58
|
-
icon: <
|
|
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: <
|
|
430
|
+
icon: <RhUiExportIcon />
|
|
432
431
|
}
|
|
433
432
|
};
|
|
434
433
|
render(<ResponseActions actions={actions} persistActionSelection />);
|
|
@@ -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,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,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,15 +0,0 @@
|
|
|
1
|
-
import { render, screen } from '@testing-library/react';
|
|
2
|
-
import '@testing-library/jest-dom';
|
|
3
|
-
import LoadingMessage from './LoadingMessage';
|
|
4
|
-
|
|
5
|
-
describe('LoadingMessage', () => {
|
|
6
|
-
it('should render loading message', () => {
|
|
7
|
-
const { container } = render(<LoadingMessage />);
|
|
8
|
-
expect(container).toMatchSnapshot();
|
|
9
|
-
});
|
|
10
|
-
it('should render loading message correctly', () => {
|
|
11
|
-
render(<LoadingMessage />);
|
|
12
|
-
expect(screen.getAllByText('.')).toBeTruthy();
|
|
13
|
-
expect(screen.getAllByText('.')).toHaveLength(3);
|
|
14
|
-
});
|
|
15
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { FunctionComponent } from 'react';
|
|
2
|
-
import { Icon, Split, SplitItem } from '@patternfly/react-core';
|
|
3
|
-
|
|
4
|
-
import RobotIcon from '@patternfly/react-icons/dist/js/icons/robot-icon';
|
|
5
|
-
|
|
6
|
-
export interface LoadingMessageProps {
|
|
7
|
-
icon?: React.ComponentType;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export const LoadingMessage: FunctionComponent<LoadingMessageProps> = ({ icon: IconComponent = RobotIcon }) => (
|
|
11
|
-
<Split>
|
|
12
|
-
<SplitItem>
|
|
13
|
-
<Icon size="lg" className="pf-v6-u-mr-sm pf-v6-u-pt-md">
|
|
14
|
-
<IconComponent />
|
|
15
|
-
</Icon>
|
|
16
|
-
</SplitItem>
|
|
17
|
-
<SplitItem className="pf-u-background-color-200">
|
|
18
|
-
<div className="pf-v6-u-display-flex pf-u-align-items-center">
|
|
19
|
-
<div>.</div>
|
|
20
|
-
<div>.</div>
|
|
21
|
-
<div>.</div>
|
|
22
|
-
</div>
|
|
23
|
-
</SplitItem>
|
|
24
|
-
</Split>
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
export default LoadingMessage;
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`LoadingMessage should render loading message 1`] = `
|
|
4
|
-
<div>
|
|
5
|
-
<div
|
|
6
|
-
class="pf-v6-l-split"
|
|
7
|
-
>
|
|
8
|
-
<div
|
|
9
|
-
class="pf-v6-l-split__item"
|
|
10
|
-
>
|
|
11
|
-
<span
|
|
12
|
-
class="pf-v6-c-icon pf-m-lg pf-v6-u-mr-sm pf-v6-u-pt-md"
|
|
13
|
-
>
|
|
14
|
-
<span
|
|
15
|
-
class="pf-v6-c-icon__content"
|
|
16
|
-
>
|
|
17
|
-
<svg
|
|
18
|
-
aria-hidden="true"
|
|
19
|
-
class="pf-v6-svg"
|
|
20
|
-
fill="currentColor"
|
|
21
|
-
height="1em"
|
|
22
|
-
role="img"
|
|
23
|
-
width="1em"
|
|
24
|
-
>
|
|
25
|
-
<svg
|
|
26
|
-
class="pf-v6-icon-default"
|
|
27
|
-
viewBox="0 0 640 512"
|
|
28
|
-
>
|
|
29
|
-
<path
|
|
30
|
-
d="M32,224H64V416H32A31.96166,31.96166,0,0,1,0,384V256A31.96166,31.96166,0,0,1,32,224Zm512-48V448a64.06328,64.06328,0,0,1-64,64H160a64.06328,64.06328,0,0,1-64-64V176a79.974,79.974,0,0,1,80-80H288V32a32,32,0,0,1,64,0V96H464A79.974,79.974,0,0,1,544,176ZM264,256a40,40,0,1,0-40,40A39.997,39.997,0,0,0,264,256Zm-8,128H192v32h64Zm96,0H288v32h64ZM456,256a40,40,0,1,0-40,40A39.997,39.997,0,0,0,456,256Zm-8,128H384v32h64ZM640,256V384a31.96166,31.96166,0,0,1-32,32H576V224h32A31.96166,31.96166,0,0,1,640,256Z"
|
|
31
|
-
/>
|
|
32
|
-
</svg>
|
|
33
|
-
<svg
|
|
34
|
-
class="pf-v6-icon-rh-ui"
|
|
35
|
-
viewBox="0 0 32 32"
|
|
36
|
-
>
|
|
37
|
-
<path
|
|
38
|
-
d="M29 22.5V29a1 1 0 1 1-2 0v-6H5v6a1 1 0 1 1-2 0v-6.5c0-.827.673-1.5 1.5-1.5h23c.827 0 1.5.673 1.5 1.5ZM3 11a1 1 0 0 1 1-1h2V5.5C6 4.673 6.673 4 7.5 4H12V2a1 1 0 1 1 2 0v2h4V2a1 1 0 1 1 2 0v2h4.5c.827 0 1.5.673 1.5 1.5V10h2a1 1 0 1 1 0 2h-2v4.5c0 .827-.673 1.5-1.5 1.5h-17c-.827 0-1.5-.673-1.5-1.5V12H4a1 1 0 0 1-1-1Zm5 5h16V6H8v10Zm5-6.612a1.626 1.626 0 1 0 .001 3.251A1.626 1.626 0 0 0 13 9.388Zm6-.01a1.626 1.626 0 1 0 .001 3.251A1.626 1.626 0 0 0 19 9.378Z"
|
|
39
|
-
/>
|
|
40
|
-
</svg>
|
|
41
|
-
</svg>
|
|
42
|
-
</span>
|
|
43
|
-
</span>
|
|
44
|
-
</div>
|
|
45
|
-
<div
|
|
46
|
-
class="pf-v6-l-split__item pf-u-background-color-200"
|
|
47
|
-
>
|
|
48
|
-
<div
|
|
49
|
-
class="pf-v6-u-display-flex pf-u-align-items-center"
|
|
50
|
-
>
|
|
51
|
-
<div>
|
|
52
|
-
.
|
|
53
|
-
</div>
|
|
54
|
-
<div>
|
|
55
|
-
.
|
|
56
|
-
</div>
|
|
57
|
-
<div>
|
|
58
|
-
.
|
|
59
|
-
</div>
|
|
60
|
-
</div>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
`;
|