@patternfly/chatbot 6.3.0-prerelease.14 → 6.3.0-prerelease.16
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/ChatbotHeader/ChatbotHeaderSelectorDropdown.d.ts +2 -0
- package/dist/cjs/ChatbotHeader/ChatbotHeaderSelectorDropdown.js +4 -2
- package/dist/cjs/MessageBox/MessageBox.d.ts +16 -4
- package/dist/cjs/MessageBox/MessageBox.js +163 -33
- package/dist/cjs/MessageBox/MessageBox.test.js +177 -4
- package/dist/esm/ChatbotHeader/ChatbotHeaderSelectorDropdown.d.ts +2 -0
- package/dist/esm/ChatbotHeader/ChatbotHeaderSelectorDropdown.js +4 -2
- package/dist/esm/MessageBox/MessageBox.d.ts +16 -4
- package/dist/esm/MessageBox/MessageBox.js +164 -34
- package/dist/esm/MessageBox/MessageBox.test.js +178 -5
- package/package.json +1 -1
- package/patternfly-docs/content/extensions/chatbot/examples/demos/AttachmentDemos.md +36 -0
- package/patternfly-docs/content/extensions/chatbot/examples/demos/ChatbotScrolling.tsx +536 -0
- package/src/ChatbotHeader/ChatbotHeaderSelectorDropdown.tsx +6 -0
- package/src/MessageBox/MessageBox.test.tsx +254 -8
- package/src/MessageBox/MessageBox.tsx +295 -88
@@ -1,9 +1,20 @@
|
|
1
1
|
import { createRef } from 'react';
|
2
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
3
|
-
import { MessageBox } from './MessageBox';
|
2
|
+
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
3
|
+
import { MessageBox, MessageBoxHandle } from './MessageBox';
|
4
4
|
import userEvent from '@testing-library/user-event';
|
5
5
|
|
6
6
|
describe('MessageBox', () => {
|
7
|
+
beforeEach(() => {
|
8
|
+
jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
|
9
|
+
cb(0); // Immediately call the callback
|
10
|
+
return 0;
|
11
|
+
});
|
12
|
+
});
|
13
|
+
|
14
|
+
afterEach(() => {
|
15
|
+
jest.restoreAllMocks();
|
16
|
+
});
|
17
|
+
|
7
18
|
it('should render Message box', () => {
|
8
19
|
render(
|
9
20
|
<MessageBox>
|
@@ -14,17 +25,25 @@ describe('MessageBox', () => {
|
|
14
25
|
});
|
15
26
|
|
16
27
|
it('should assign ref to Message box', () => {
|
17
|
-
const ref = createRef<
|
28
|
+
const ref = createRef<MessageBoxHandle>();
|
18
29
|
render(
|
19
|
-
<MessageBox ref={ref}>
|
30
|
+
<MessageBox data-testid="message-box" ref={ref}>
|
20
31
|
<div>Test message content</div>
|
21
32
|
</MessageBox>
|
22
33
|
);
|
23
34
|
|
35
|
+
screen.getByText('Test message content');
|
36
|
+
|
24
37
|
expect(ref.current).not.toBeNull();
|
38
|
+
// should contain custom methods exposed by the ref
|
39
|
+
expect(typeof ref.current?.isSmartScrollActive).toBe('function');
|
40
|
+
expect(typeof ref.current?.scrollToTop).toBe('function');
|
41
|
+
expect(typeof ref.current?.scrollToBottom).toBe('function');
|
42
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
43
|
+
|
25
44
|
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
26
45
|
});
|
27
|
-
it('should call onScrollToBottomClick when scroll to
|
46
|
+
it('should call onScrollToBottomClick when scroll to bottom button is clicked', async () => {
|
28
47
|
const spy = jest.fn();
|
29
48
|
render(
|
30
49
|
<MessageBox onScrollToBottomClick={spy}>
|
@@ -37,7 +56,9 @@ describe('MessageBox', () => {
|
|
37
56
|
Object.defineProperty(region, 'scrollHeight', { configurable: true, value: 1000 });
|
38
57
|
Object.defineProperty(region, 'clientHeight', { configurable: true, value: 500 });
|
39
58
|
Object.defineProperty(region, 'scrollTop', { configurable: true, value: 0 });
|
40
|
-
|
59
|
+
act(() => {
|
60
|
+
region.dispatchEvent(new Event('scroll'));
|
61
|
+
});
|
41
62
|
|
42
63
|
await waitFor(() => {
|
43
64
|
userEvent.click(screen.getByRole('button', { name: /Jump bottom/i }));
|
@@ -60,11 +81,236 @@ describe('MessageBox', () => {
|
|
60
81
|
configurable: true,
|
61
82
|
value: 500
|
62
83
|
});
|
63
|
-
|
64
|
-
|
84
|
+
act(() => {
|
85
|
+
region.dispatchEvent(new Event('scroll'));
|
86
|
+
});
|
65
87
|
await waitFor(() => {
|
66
88
|
userEvent.click(screen.getByRole('button', { name: /Jump top/i }));
|
67
89
|
expect(spy).toHaveBeenCalled();
|
68
90
|
});
|
69
91
|
});
|
92
|
+
|
93
|
+
it('should call user defined onWheel, onTouchMove and onTouchEnd handlers', async () => {
|
94
|
+
const ref = createRef<MessageBoxHandle>();
|
95
|
+
const onWheel = jest.fn();
|
96
|
+
const onTouchMove = jest.fn();
|
97
|
+
const onTouchEnd = jest.fn();
|
98
|
+
|
99
|
+
render(
|
100
|
+
<MessageBox ref={ref} enableSmartScroll onWheel={onWheel} onTouchMove={onTouchMove} onTouchEnd={onTouchEnd}>
|
101
|
+
<div>Test message content</div>
|
102
|
+
</MessageBox>
|
103
|
+
);
|
104
|
+
|
105
|
+
const element = ref.current!;
|
106
|
+
act(() => {
|
107
|
+
fireEvent.wheel(element, { deltaY: 10 });
|
108
|
+
fireEvent.touchMove(element, { touches: [{ clientY: 700 }] });
|
109
|
+
fireEvent.touchEnd(element);
|
110
|
+
});
|
111
|
+
|
112
|
+
expect(onWheel).toHaveBeenCalled();
|
113
|
+
expect(onTouchMove).toHaveBeenCalled();
|
114
|
+
expect(onTouchEnd).toHaveBeenCalled();
|
115
|
+
});
|
116
|
+
|
117
|
+
it('should scroll to the bottom when the method is called ', async () => {
|
118
|
+
const ref = createRef<MessageBoxHandle>();
|
119
|
+
|
120
|
+
render(
|
121
|
+
<MessageBox ref={ref} enableSmartScroll>
|
122
|
+
<div>Test message content</div>
|
123
|
+
</MessageBox>
|
124
|
+
);
|
125
|
+
const element = ref.current!;
|
126
|
+
const scrollSpy = jest.spyOn(element, 'scrollTo');
|
127
|
+
|
128
|
+
act(() => {
|
129
|
+
ref.current?.scrollToBottom();
|
130
|
+
ref.current?.scrollToBottom();
|
131
|
+
ref.current?.scrollToBottom();
|
132
|
+
});
|
133
|
+
|
134
|
+
expect(scrollSpy).toHaveBeenCalledWith({ top: element.scrollHeight, behavior: 'smooth' });
|
135
|
+
expect(ref.current?.isSmartScrollActive()).toBe(true);
|
136
|
+
});
|
137
|
+
|
138
|
+
it('should scroll to the top when the method is called ', async () => {
|
139
|
+
const ref = createRef<MessageBoxHandle>();
|
140
|
+
|
141
|
+
render(
|
142
|
+
<MessageBox ref={ref} enableSmartScroll>
|
143
|
+
<div>Test message content</div>
|
144
|
+
</MessageBox>
|
145
|
+
);
|
146
|
+
const element = ref.current!;
|
147
|
+
const scrollSpy = jest.spyOn(element, 'scrollTo');
|
148
|
+
|
149
|
+
act(() => {
|
150
|
+
ref.current?.scrollToTop();
|
151
|
+
});
|
152
|
+
|
153
|
+
expect(scrollSpy).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
|
154
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
155
|
+
});
|
156
|
+
|
157
|
+
it('should pause automatic scrolling when user scrolls up ', async () => {
|
158
|
+
const ref = createRef<MessageBoxHandle>();
|
159
|
+
|
160
|
+
render(
|
161
|
+
<MessageBox ref={ref} enableSmartScroll>
|
162
|
+
<div>Test message content</div>
|
163
|
+
</MessageBox>
|
164
|
+
);
|
165
|
+
|
166
|
+
expect(ref.current?.isSmartScrollActive()).toBe(true);
|
167
|
+
|
168
|
+
const element = ref.current!;
|
169
|
+
|
170
|
+
// Manually set scrollHeight and clientHeight for calculations
|
171
|
+
Object.defineProperty(element, 'scrollHeight', { configurable: true, value: 1000 });
|
172
|
+
Object.defineProperty(element, 'clientHeight', { configurable: true, value: 300 });
|
173
|
+
|
174
|
+
// Simulate scroll up by changing scrollTop
|
175
|
+
element.scrollTop = 200;
|
176
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
177
|
+
|
178
|
+
act(() => {
|
179
|
+
element.dispatchEvent(scrollEvent);
|
180
|
+
});
|
181
|
+
|
182
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
183
|
+
});
|
184
|
+
|
185
|
+
it('should resume automatic scrolling when user scrolls down to the bottom using scroll event', async () => {
|
186
|
+
jest.useFakeTimers();
|
187
|
+
|
188
|
+
const ref = createRef<MessageBoxHandle>();
|
189
|
+
render(
|
190
|
+
<MessageBox ref={ref} enableSmartScroll>
|
191
|
+
<div>Test message content</div>
|
192
|
+
</MessageBox>
|
193
|
+
);
|
194
|
+
|
195
|
+
const element = ref.current!;
|
196
|
+
// Manually set scrollHeight and clientHeight for calculations
|
197
|
+
Object.defineProperty(element, 'scrollHeight', { configurable: true, value: 1000 });
|
198
|
+
Object.defineProperty(element, 'clientHeight', { configurable: true, value: 300 });
|
199
|
+
|
200
|
+
// Simulate scroll up by changing scrollTop
|
201
|
+
element.scrollTop = 100;
|
202
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
203
|
+
|
204
|
+
act(() => {
|
205
|
+
element.dispatchEvent(scrollEvent);
|
206
|
+
});
|
207
|
+
|
208
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
209
|
+
|
210
|
+
act(() => {
|
211
|
+
// Simulate scroll down by changing scrollTop
|
212
|
+
element.scrollTop = 650; // scrollHeight - scrollTop - clientHeight - DELTA_DOWN (50) = 0
|
213
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
214
|
+
element.dispatchEvent(scrollEvent);
|
215
|
+
jest.advanceTimersByTime(250);
|
216
|
+
});
|
217
|
+
|
218
|
+
expect(ref.current?.isSmartScrollActive()).toBe(true);
|
219
|
+
jest.useRealTimers();
|
220
|
+
});
|
221
|
+
|
222
|
+
it('should resume automatic scrolling when scrollToBottom method is used', async () => {
|
223
|
+
const ref = createRef<MessageBoxHandle>();
|
224
|
+
render(
|
225
|
+
<MessageBox ref={ref} enableSmartScroll>
|
226
|
+
<div>Test message content</div>
|
227
|
+
</MessageBox>
|
228
|
+
);
|
229
|
+
|
230
|
+
const element = ref.current!;
|
231
|
+
// Manually set scrollHeight and clientHeight for calculations
|
232
|
+
Object.defineProperty(element, 'scrollHeight', { configurable: true, value: 1000 });
|
233
|
+
Object.defineProperty(element, 'clientHeight', { configurable: true, value: 300 });
|
234
|
+
|
235
|
+
// Simulate scroll up by changing scrollTop
|
236
|
+
element.scrollTop = 100;
|
237
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
238
|
+
|
239
|
+
act(() => {
|
240
|
+
element.dispatchEvent(scrollEvent);
|
241
|
+
});
|
242
|
+
|
243
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
244
|
+
|
245
|
+
act(() => {
|
246
|
+
ref.current?.scrollToBottom({ resumeSmartScroll: true, behavior: 'auto' }); // resumes auto scroll and scrolls to bottom.
|
247
|
+
});
|
248
|
+
|
249
|
+
expect(ref.current?.isSmartScrollActive()).toBe(true);
|
250
|
+
});
|
251
|
+
|
252
|
+
it('should resume automatic scrolling when mouse wheel event is used', async () => {
|
253
|
+
const ref = createRef<MessageBoxHandle>();
|
254
|
+
render(
|
255
|
+
<MessageBox ref={ref} enableSmartScroll>
|
256
|
+
<div>Test message content</div>
|
257
|
+
</MessageBox>
|
258
|
+
);
|
259
|
+
|
260
|
+
const element = ref.current!;
|
261
|
+
// Manually set scrollHeight and clientHeight for calculations
|
262
|
+
Object.defineProperty(element, 'scrollHeight', { configurable: true, value: 1000 });
|
263
|
+
Object.defineProperty(element, 'clientHeight', { configurable: true, value: 300 });
|
264
|
+
Object.defineProperty(element, 'scrollTop', { configurable: true, value: 350 });
|
265
|
+
|
266
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
267
|
+
|
268
|
+
act(() => {
|
269
|
+
element.dispatchEvent(scrollEvent);
|
270
|
+
});
|
271
|
+
|
272
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
273
|
+
|
274
|
+
// Simulate mouse wheel event
|
275
|
+
act(() => {
|
276
|
+
Object.defineProperty(element, 'scrollTop', { configurable: true, value: 650 });
|
277
|
+
fireEvent.wheel(element, { deltaY: 10 });
|
278
|
+
});
|
279
|
+
|
280
|
+
expect(ref.current?.isSmartScrollActive()).toBe(true);
|
281
|
+
});
|
282
|
+
|
283
|
+
it('should resume automatic scrolling when user swipes up in touch screen', async () => {
|
284
|
+
const ref = createRef<MessageBoxHandle>();
|
285
|
+
render(
|
286
|
+
<MessageBox ref={ref} enableSmartScroll>
|
287
|
+
<div>Test message content</div>
|
288
|
+
</MessageBox>
|
289
|
+
);
|
290
|
+
|
291
|
+
const element = ref.current!;
|
292
|
+
// Manually set scrollHeight and clientHeight for calculations
|
293
|
+
Object.defineProperty(element, 'scrollHeight', { configurable: true, value: 1000 });
|
294
|
+
Object.defineProperty(element, 'clientHeight', { configurable: true, value: 300 });
|
295
|
+
Object.defineProperty(element, 'scrollTop', { configurable: true, value: 350 });
|
296
|
+
|
297
|
+
const scrollEvent = new Event('scroll', { bubbles: true });
|
298
|
+
|
299
|
+
act(() => {
|
300
|
+
element.dispatchEvent(scrollEvent);
|
301
|
+
});
|
302
|
+
|
303
|
+
expect(ref.current?.isSmartScrollActive()).toBe(false);
|
304
|
+
|
305
|
+
// Simulate touch event - swipe up
|
306
|
+
act(() => {
|
307
|
+
Object.defineProperty(element, 'scrollTop', { configurable: true, value: 650 });
|
308
|
+
fireEvent.touchStart(element, { touches: [{ clientY: 700 }] });
|
309
|
+
fireEvent.touchMove(element, { touches: [{ clientY: 700 }] });
|
310
|
+
fireEvent.touchMove(element, { touches: [{ clientY: 600 }] });
|
311
|
+
fireEvent.touchEnd(element);
|
312
|
+
});
|
313
|
+
|
314
|
+
expect(ref.current?.isSmartScrollActive()).toBe(true);
|
315
|
+
});
|
70
316
|
});
|