@assistant-ui/react-native 0.1.41 → 0.1.42
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/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/primitives/composer/ComposerAddAttachment.d.ts +3 -5
- package/dist/primitives/composer/ComposerAddAttachment.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAddAttachment.js +2 -4
- package/dist/primitives/composer/ComposerAddAttachment.js.map +1 -1
- package/dist/primitives/thread/ThreadMessages.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadMessages.js +186 -152
- package/dist/primitives/thread/ThreadMessages.js.map +1 -1
- package/package.json +6 -5
- package/src/index.ts +29 -0
- package/src/primitives/composer/ComposerAddAttachment.tsx +3 -8
- package/src/primitives/thread/ThreadMessages.test.tsx +256 -53
- package/src/primitives/thread/ThreadMessages.tsx +67 -26
|
@@ -15,7 +15,7 @@ const h = vi.hoisted(() => ({
|
|
|
15
15
|
itemState: { role: "user" } as { role: string },
|
|
16
16
|
events: {} as Record<string, Set<() => void>>,
|
|
17
17
|
flatListProps: null as Record<string, unknown> | null,
|
|
18
|
-
|
|
18
|
+
scrollToOffset: vi.fn(),
|
|
19
19
|
}));
|
|
20
20
|
|
|
21
21
|
vi.mock("react-native", async (importOriginal) => {
|
|
@@ -29,7 +29,7 @@ vi.mock("react-native", async (importOriginal) => {
|
|
|
29
29
|
h.flatListProps = props;
|
|
30
30
|
|
|
31
31
|
React.useImperativeHandle(ref, () => ({
|
|
32
|
-
|
|
32
|
+
scrollToOffset: h.scrollToOffset,
|
|
33
33
|
}));
|
|
34
34
|
|
|
35
35
|
const data = (props.data as unknown[]) ?? [];
|
|
@@ -121,7 +121,7 @@ describe("ThreadMessages", () => {
|
|
|
121
121
|
h.itemState = { role: "user" };
|
|
122
122
|
h.events = {};
|
|
123
123
|
h.flatListProps = null;
|
|
124
|
-
h.
|
|
124
|
+
h.scrollToOffset.mockReset();
|
|
125
125
|
|
|
126
126
|
container = document.createElement("div");
|
|
127
127
|
document.body.appendChild(container);
|
|
@@ -338,6 +338,25 @@ describe("ThreadMessages", () => {
|
|
|
338
338
|
expect(ref.current).not.toBeNull();
|
|
339
339
|
});
|
|
340
340
|
|
|
341
|
+
it("anchors the visible message while content is inserted above it", async () => {
|
|
342
|
+
await mountFlatList({ children: () => null });
|
|
343
|
+
|
|
344
|
+
expect(h.flatListProps?.maintainVisibleContentPosition).toEqual({
|
|
345
|
+
minIndexForVisible: 0,
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("lets the app override the visible content anchor", async () => {
|
|
350
|
+
await mountFlatList({
|
|
351
|
+
children: () => null,
|
|
352
|
+
maintainVisibleContentPosition: { minIndexForVisible: 2 },
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
expect(h.flatListProps?.maintainVisibleContentPosition).toEqual({
|
|
356
|
+
minIndexForVisible: 2,
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
341
360
|
it("keeps deprecated Messages off the scroll-tracking path", async () => {
|
|
342
361
|
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
343
362
|
await mount({ components: messageComponents });
|
|
@@ -370,7 +389,7 @@ describe("ThreadMessages", () => {
|
|
|
370
389
|
await emit("thread.runStart");
|
|
371
390
|
await emit("threads.selectionChanged");
|
|
372
391
|
|
|
373
|
-
expect(h.
|
|
392
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
374
393
|
});
|
|
375
394
|
|
|
376
395
|
describe("MessagesFlatList auto-scroll", () => {
|
|
@@ -379,24 +398,30 @@ describe("ThreadMessages", () => {
|
|
|
379
398
|
|
|
380
399
|
await mountFlatList({ components: messageComponents });
|
|
381
400
|
|
|
382
|
-
expect(h.
|
|
401
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
402
|
+
animated: false,
|
|
403
|
+
offset: 0,
|
|
404
|
+
});
|
|
383
405
|
});
|
|
384
406
|
|
|
385
407
|
it("scrolls to the bottom when a run starts", async () => {
|
|
386
408
|
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
387
409
|
await mountFlatList({ components: messageComponents });
|
|
388
|
-
h.
|
|
410
|
+
h.scrollToOffset.mockClear();
|
|
389
411
|
|
|
390
412
|
await emit("thread.runStart");
|
|
391
413
|
|
|
392
|
-
expect(h.
|
|
414
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
415
|
+
animated: true,
|
|
416
|
+
offset: 0,
|
|
417
|
+
});
|
|
393
418
|
});
|
|
394
419
|
|
|
395
420
|
it("scrolls when content grows while already at the bottom", async () => {
|
|
396
421
|
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
397
422
|
await mountFlatList({ components: messageComponents });
|
|
398
423
|
const props = getFlatListProps();
|
|
399
|
-
h.
|
|
424
|
+
h.scrollToOffset.mockClear();
|
|
400
425
|
|
|
401
426
|
await act(async () => {
|
|
402
427
|
props.onLayout?.({
|
|
@@ -412,7 +437,10 @@ describe("ThreadMessages", () => {
|
|
|
412
437
|
props.onContentSizeChange?.(0, 140);
|
|
413
438
|
});
|
|
414
439
|
|
|
415
|
-
expect(h.
|
|
440
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
441
|
+
animated: false,
|
|
442
|
+
offset: 40,
|
|
443
|
+
});
|
|
416
444
|
});
|
|
417
445
|
|
|
418
446
|
it("does not treat the first content-size event as automatic content growth", async () => {
|
|
@@ -427,7 +455,7 @@ describe("ThreadMessages", () => {
|
|
|
427
455
|
props.onContentSizeChange?.(0, 140);
|
|
428
456
|
});
|
|
429
457
|
|
|
430
|
-
expect(h.
|
|
458
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
431
459
|
});
|
|
432
460
|
|
|
433
461
|
it("lands the initialize scroll on the first content-size event", async () => {
|
|
@@ -435,35 +463,151 @@ describe("ThreadMessages", () => {
|
|
|
435
463
|
await mountFlatList({ components: messageComponents });
|
|
436
464
|
const props = getFlatListProps();
|
|
437
465
|
|
|
438
|
-
expect(h.
|
|
466
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
467
|
+
|
|
468
|
+
await act(async () => {
|
|
469
|
+
props.onLayout?.({
|
|
470
|
+
nativeEvent: { layout: { height: 100 } },
|
|
471
|
+
});
|
|
472
|
+
props.onContentSizeChange?.(0, 140);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
476
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
477
|
+
animated: false,
|
|
478
|
+
offset: 40,
|
|
479
|
+
});
|
|
439
480
|
|
|
440
481
|
await act(async () => {
|
|
441
482
|
props.onContentSizeChange?.(0, 140);
|
|
442
483
|
});
|
|
443
484
|
|
|
444
|
-
expect(h.
|
|
485
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it("waits for the first layout before consuming a measured initialize scroll", async () => {
|
|
489
|
+
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
490
|
+
await mountFlatList({ components: messageComponents });
|
|
491
|
+
const props = getFlatListProps();
|
|
445
492
|
|
|
446
493
|
await act(async () => {
|
|
447
494
|
props.onContentSizeChange?.(0, 140);
|
|
448
495
|
});
|
|
496
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
449
497
|
|
|
450
|
-
|
|
498
|
+
await act(async () => {
|
|
499
|
+
props.onLayout?.({
|
|
500
|
+
nativeEvent: { layout: { height: 100 } },
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
505
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
506
|
+
animated: false,
|
|
507
|
+
offset: 40,
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("keeps the initialize scroll pending through a zero content measurement", async () => {
|
|
512
|
+
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
513
|
+
await mountFlatList({ components: messageComponents });
|
|
514
|
+
const props = getFlatListProps();
|
|
515
|
+
|
|
516
|
+
await act(async () => {
|
|
517
|
+
props.onLayout?.({
|
|
518
|
+
nativeEvent: { layout: { height: 100 } },
|
|
519
|
+
});
|
|
520
|
+
props.onContentSizeChange?.(0, 0);
|
|
521
|
+
});
|
|
522
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
523
|
+
|
|
524
|
+
await act(async () => {
|
|
525
|
+
props.onContentSizeChange?.(0, 300);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
529
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
530
|
+
animated: false,
|
|
531
|
+
offset: 200,
|
|
532
|
+
});
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it("uses horizontal measurements for horizontal lists", async () => {
|
|
536
|
+
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
537
|
+
await mountFlatList({ components: messageComponents, horizontal: true });
|
|
538
|
+
const props = getFlatListProps();
|
|
539
|
+
|
|
540
|
+
await act(async () => {
|
|
541
|
+
props.onLayout?.({
|
|
542
|
+
nativeEvent: { layout: { width: 100 } },
|
|
543
|
+
});
|
|
544
|
+
props.onContentSizeChange?.(140, 0);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
548
|
+
animated: false,
|
|
549
|
+
offset: 40,
|
|
550
|
+
});
|
|
451
551
|
});
|
|
452
552
|
|
|
453
553
|
it("lands the thread-switch scroll on the next content-size event", async () => {
|
|
454
554
|
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
455
555
|
await mountFlatList({ components: messageComponents });
|
|
456
556
|
const props = getFlatListProps();
|
|
457
|
-
h.
|
|
557
|
+
h.scrollToOffset.mockClear();
|
|
458
558
|
|
|
459
559
|
await emit("threads.selectionChanged");
|
|
460
|
-
expect(h.
|
|
560
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
461
561
|
|
|
462
562
|
await act(async () => {
|
|
463
|
-
props.onContentSizeChange?.(0,
|
|
563
|
+
props.onContentSizeChange?.(0, 300);
|
|
464
564
|
});
|
|
565
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
465
566
|
|
|
466
|
-
|
|
567
|
+
await act(async () => {
|
|
568
|
+
props.onLayout?.({
|
|
569
|
+
nativeEvent: { layout: { height: 100 } },
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
574
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
575
|
+
animated: false,
|
|
576
|
+
offset: 200,
|
|
577
|
+
});
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("does not consume a thread-switch scroll with the previous thread metrics", async () => {
|
|
581
|
+
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
582
|
+
await mountFlatList({ components: messageComponents });
|
|
583
|
+
const props = getFlatListProps();
|
|
584
|
+
|
|
585
|
+
await act(async () => {
|
|
586
|
+
props.onLayout?.({
|
|
587
|
+
nativeEvent: { layout: { height: 100 } },
|
|
588
|
+
});
|
|
589
|
+
props.onScroll?.({
|
|
590
|
+
nativeEvent: {
|
|
591
|
+
contentOffset: { y: 200 },
|
|
592
|
+
contentSize: { height: 300, width: 0 },
|
|
593
|
+
layoutMeasurement: { height: 100, width: 0 },
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
h.scrollToOffset.mockClear();
|
|
598
|
+
|
|
599
|
+
await emit("threads.selectionChanged");
|
|
600
|
+
await act(async () => {
|
|
601
|
+
props.onLayout?.({
|
|
602
|
+
nativeEvent: { layout: { height: 80 } },
|
|
603
|
+
});
|
|
604
|
+
props.onContentSizeChange?.(0, 260);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
608
|
+
animated: false,
|
|
609
|
+
offset: 180,
|
|
610
|
+
});
|
|
467
611
|
});
|
|
468
612
|
|
|
469
613
|
it("keeps following through consecutive growth events without scroll echoes", async () => {
|
|
@@ -483,7 +627,7 @@ describe("ThreadMessages", () => {
|
|
|
483
627
|
},
|
|
484
628
|
});
|
|
485
629
|
});
|
|
486
|
-
h.
|
|
630
|
+
h.scrollToOffset.mockClear();
|
|
487
631
|
|
|
488
632
|
await act(async () => {
|
|
489
633
|
props.onContentSizeChange?.(0, 140);
|
|
@@ -492,14 +636,14 @@ describe("ThreadMessages", () => {
|
|
|
492
636
|
props.onContentSizeChange?.(0, 180);
|
|
493
637
|
});
|
|
494
638
|
|
|
495
|
-
expect(h.
|
|
639
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
496
640
|
});
|
|
497
641
|
|
|
498
642
|
it("does not scroll when content grows after the user scrolled away", async () => {
|
|
499
643
|
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
500
644
|
await mountFlatList({ components: messageComponents });
|
|
501
645
|
const props = getFlatListProps();
|
|
502
|
-
h.
|
|
646
|
+
h.scrollToOffset.mockClear();
|
|
503
647
|
|
|
504
648
|
await act(async () => {
|
|
505
649
|
props.onLayout?.({
|
|
@@ -522,7 +666,7 @@ describe("ThreadMessages", () => {
|
|
|
522
666
|
props.onContentSizeChange?.(0, 340);
|
|
523
667
|
});
|
|
524
668
|
|
|
525
|
-
expect(h.
|
|
669
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
526
670
|
});
|
|
527
671
|
|
|
528
672
|
it("lands the run-start scroll once the appended message resizes content", async () => {
|
|
@@ -549,10 +693,10 @@ describe("ThreadMessages", () => {
|
|
|
549
693
|
},
|
|
550
694
|
});
|
|
551
695
|
});
|
|
552
|
-
h.
|
|
696
|
+
h.scrollToOffset.mockClear();
|
|
553
697
|
|
|
554
698
|
await emit("thread.runStart");
|
|
555
|
-
expect(h.
|
|
699
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
556
700
|
|
|
557
701
|
await act(async () => {
|
|
558
702
|
props.onScroll?.({
|
|
@@ -565,18 +709,39 @@ describe("ThreadMessages", () => {
|
|
|
565
709
|
props.onContentSizeChange?.(0, 360);
|
|
566
710
|
});
|
|
567
711
|
|
|
568
|
-
expect(h.
|
|
569
|
-
expect(h.
|
|
712
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
713
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
714
|
+
animated: true,
|
|
715
|
+
offset: 260,
|
|
716
|
+
});
|
|
570
717
|
});
|
|
571
718
|
|
|
572
|
-
it("
|
|
719
|
+
it("uses the measured bottom when a thread switch does not resize content", async () => {
|
|
573
720
|
h.state.thread.messages = [{ id: "1", role: "user" }];
|
|
574
721
|
await mountFlatList({ components: messageComponents });
|
|
575
|
-
|
|
722
|
+
const props = getFlatListProps();
|
|
723
|
+
|
|
724
|
+
await act(async () => {
|
|
725
|
+
props.onLayout?.({
|
|
726
|
+
nativeEvent: { layout: { height: 100 } },
|
|
727
|
+
});
|
|
728
|
+
props.onScroll?.({
|
|
729
|
+
nativeEvent: {
|
|
730
|
+
contentOffset: { y: 120 },
|
|
731
|
+
contentSize: { height: 300, width: 0 },
|
|
732
|
+
layoutMeasurement: { height: 100, width: 0 },
|
|
733
|
+
},
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
h.scrollToOffset.mockClear();
|
|
576
737
|
|
|
577
738
|
await emit("threads.selectionChanged");
|
|
578
739
|
|
|
579
|
-
expect(h.
|
|
740
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(1);
|
|
741
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
742
|
+
animated: false,
|
|
743
|
+
offset: 200,
|
|
744
|
+
});
|
|
580
745
|
});
|
|
581
746
|
|
|
582
747
|
it("does not rearm initialize scroll when thread-switch scroll is disabled", async () => {
|
|
@@ -585,7 +750,7 @@ describe("ThreadMessages", () => {
|
|
|
585
750
|
components: messageComponents,
|
|
586
751
|
scrollToBottomOnThreadSwitch: false,
|
|
587
752
|
});
|
|
588
|
-
h.
|
|
753
|
+
h.scrollToOffset.mockClear();
|
|
589
754
|
|
|
590
755
|
await emit("threads.selectionChanged");
|
|
591
756
|
|
|
@@ -598,7 +763,7 @@ describe("ThreadMessages", () => {
|
|
|
598
763
|
scrollToBottomOnThreadSwitch: false,
|
|
599
764
|
});
|
|
600
765
|
|
|
601
|
-
expect(h.
|
|
766
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
602
767
|
});
|
|
603
768
|
|
|
604
769
|
it("honors opt-outs for automatic content growth and run-start scrolls", async () => {
|
|
@@ -610,7 +775,7 @@ describe("ThreadMessages", () => {
|
|
|
610
775
|
scrollToBottomOnRunStart: false,
|
|
611
776
|
});
|
|
612
777
|
const props = getFlatListProps();
|
|
613
|
-
h.
|
|
778
|
+
h.scrollToOffset.mockClear();
|
|
614
779
|
|
|
615
780
|
await act(async () => {
|
|
616
781
|
props.onLayout?.({
|
|
@@ -627,7 +792,7 @@ describe("ThreadMessages", () => {
|
|
|
627
792
|
});
|
|
628
793
|
await emit("thread.runStart");
|
|
629
794
|
|
|
630
|
-
expect(h.
|
|
795
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
631
796
|
});
|
|
632
797
|
|
|
633
798
|
it("sets a useful default scroll throttle", async () => {
|
|
@@ -655,10 +820,32 @@ describe("ThreadMessages", () => {
|
|
|
655
820
|
});
|
|
656
821
|
props.onContentSizeChange?.(0, 300);
|
|
657
822
|
});
|
|
658
|
-
h.
|
|
823
|
+
h.scrollToOffset.mockClear();
|
|
659
824
|
return props;
|
|
660
825
|
};
|
|
661
826
|
|
|
827
|
+
it("stays pinned when an anchor adjustment moves the offset with the content above it", async () => {
|
|
828
|
+
const props = await mountPinned();
|
|
829
|
+
|
|
830
|
+
await act(async () => {
|
|
831
|
+
props.onScroll?.({
|
|
832
|
+
nativeEvent: {
|
|
833
|
+
contentOffset: { y: 180 },
|
|
834
|
+
contentSize: { height: 280, width: 0 },
|
|
835
|
+
layoutMeasurement: { height: 100, width: 0 },
|
|
836
|
+
},
|
|
837
|
+
});
|
|
838
|
+
});
|
|
839
|
+
await act(async () => {
|
|
840
|
+
props.onContentSizeChange?.(0, 320);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
844
|
+
animated: false,
|
|
845
|
+
offset: 220,
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
|
|
662
849
|
it("stays pinned when the viewport shrinks while at the bottom", async () => {
|
|
663
850
|
const props = await mountPinned();
|
|
664
851
|
|
|
@@ -667,12 +854,15 @@ describe("ThreadMessages", () => {
|
|
|
667
854
|
nativeEvent: { layout: { height: 60 } },
|
|
668
855
|
});
|
|
669
856
|
});
|
|
670
|
-
h.
|
|
857
|
+
h.scrollToOffset.mockClear();
|
|
671
858
|
await act(async () => {
|
|
672
859
|
props.onContentSizeChange?.(0, 340);
|
|
673
860
|
});
|
|
674
861
|
|
|
675
|
-
expect(h.
|
|
862
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
863
|
+
animated: false,
|
|
864
|
+
offset: 280,
|
|
865
|
+
});
|
|
676
866
|
});
|
|
677
867
|
|
|
678
868
|
it("commands a bottom scroll when the viewport shrinks while pinned", async () => {
|
|
@@ -684,22 +874,29 @@ describe("ThreadMessages", () => {
|
|
|
684
874
|
});
|
|
685
875
|
});
|
|
686
876
|
|
|
687
|
-
expect(h.
|
|
877
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
878
|
+
animated: false,
|
|
879
|
+
offset: 240,
|
|
880
|
+
});
|
|
688
881
|
});
|
|
689
882
|
|
|
690
883
|
it("preserves a pending animated scroll on a pinned viewport change", async () => {
|
|
691
884
|
const props = await mountPinned();
|
|
692
885
|
|
|
693
886
|
await emit("thread.runStart");
|
|
694
|
-
h.
|
|
887
|
+
h.scrollToOffset.mockClear();
|
|
695
888
|
await act(async () => {
|
|
696
889
|
props.onLayout?.({
|
|
697
890
|
nativeEvent: { layout: { height: 60 } },
|
|
698
891
|
});
|
|
892
|
+
props.onContentSizeChange?.(0, 360);
|
|
699
893
|
});
|
|
700
894
|
|
|
701
|
-
expect(h.
|
|
702
|
-
expect(h.
|
|
895
|
+
expect(h.scrollToOffset).toHaveBeenCalledTimes(2);
|
|
896
|
+
expect(h.scrollToOffset).toHaveBeenLastCalledWith({
|
|
897
|
+
animated: true,
|
|
898
|
+
offset: 300,
|
|
899
|
+
});
|
|
703
900
|
});
|
|
704
901
|
|
|
705
902
|
it("ignores a layout event with an unchanged viewport height", async () => {
|
|
@@ -711,13 +908,13 @@ describe("ThreadMessages", () => {
|
|
|
711
908
|
});
|
|
712
909
|
});
|
|
713
910
|
|
|
714
|
-
expect(h.
|
|
911
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
715
912
|
});
|
|
716
913
|
|
|
717
914
|
it("does not command a scroll on the first layout measurement", async () => {
|
|
718
915
|
await mountFlatList({ components: messageComponents });
|
|
719
916
|
getFlatListProps();
|
|
720
|
-
h.
|
|
917
|
+
h.scrollToOffset.mockClear();
|
|
721
918
|
|
|
722
919
|
await act(async () => {
|
|
723
920
|
getFlatListProps().onLayout?.({
|
|
@@ -725,7 +922,7 @@ describe("ThreadMessages", () => {
|
|
|
725
922
|
});
|
|
726
923
|
});
|
|
727
924
|
|
|
728
|
-
expect(h.
|
|
925
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
729
926
|
});
|
|
730
927
|
|
|
731
928
|
it("does not command a scroll on viewport change when autoScroll is off", async () => {
|
|
@@ -744,7 +941,7 @@ describe("ThreadMessages", () => {
|
|
|
744
941
|
});
|
|
745
942
|
props.onContentSizeChange?.(0, 300);
|
|
746
943
|
});
|
|
747
|
-
h.
|
|
944
|
+
h.scrollToOffset.mockClear();
|
|
748
945
|
|
|
749
946
|
await act(async () => {
|
|
750
947
|
props.onLayout?.({
|
|
@@ -752,7 +949,7 @@ describe("ThreadMessages", () => {
|
|
|
752
949
|
});
|
|
753
950
|
});
|
|
754
951
|
|
|
755
|
-
expect(h.
|
|
952
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
756
953
|
});
|
|
757
954
|
|
|
758
955
|
it("stays unpinned when the viewport shrinks after scrolling away", async () => {
|
|
@@ -776,7 +973,7 @@ describe("ThreadMessages", () => {
|
|
|
776
973
|
props.onContentSizeChange?.(0, 340);
|
|
777
974
|
});
|
|
778
975
|
|
|
779
|
-
expect(h.
|
|
976
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
780
977
|
});
|
|
781
978
|
|
|
782
979
|
it("keeps the pin through a downward scroll echo after a commanded scroll", async () => {
|
|
@@ -795,7 +992,7 @@ describe("ThreadMessages", () => {
|
|
|
795
992
|
await act(async () => {
|
|
796
993
|
props.onContentSizeChange?.(0, 320);
|
|
797
994
|
});
|
|
798
|
-
h.
|
|
995
|
+
h.scrollToOffset.mockClear();
|
|
799
996
|
await act(async () => {
|
|
800
997
|
props.onScroll?.({
|
|
801
998
|
nativeEvent: {
|
|
@@ -809,14 +1006,17 @@ describe("ThreadMessages", () => {
|
|
|
809
1006
|
props.onContentSizeChange?.(0, 360);
|
|
810
1007
|
});
|
|
811
1008
|
|
|
812
|
-
expect(h.
|
|
1009
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
1010
|
+
animated: false,
|
|
1011
|
+
offset: 260,
|
|
1012
|
+
});
|
|
813
1013
|
});
|
|
814
1014
|
|
|
815
1015
|
it("unpins and cancels a pending scroll on an upward gesture echo", async () => {
|
|
816
1016
|
const props = await mountPinned();
|
|
817
1017
|
|
|
818
1018
|
await emit("thread.runStart");
|
|
819
|
-
h.
|
|
1019
|
+
h.scrollToOffset.mockClear();
|
|
820
1020
|
await act(async () => {
|
|
821
1021
|
props.onScroll?.({
|
|
822
1022
|
nativeEvent: {
|
|
@@ -830,17 +1030,20 @@ describe("ThreadMessages", () => {
|
|
|
830
1030
|
props.onContentSizeChange?.(0, 360);
|
|
831
1031
|
});
|
|
832
1032
|
|
|
833
|
-
expect(h.
|
|
1033
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
834
1034
|
});
|
|
835
1035
|
|
|
836
1036
|
it("scrolls on content growth while pinned and stays put while unpinned", async () => {
|
|
837
1037
|
const props = await mountPinned();
|
|
838
|
-
h.
|
|
1038
|
+
h.scrollToOffset.mockClear();
|
|
839
1039
|
|
|
840
1040
|
await act(async () => {
|
|
841
1041
|
props.onContentSizeChange?.(0, 340);
|
|
842
1042
|
});
|
|
843
|
-
expect(h.
|
|
1043
|
+
expect(h.scrollToOffset).toHaveBeenCalledWith({
|
|
1044
|
+
animated: false,
|
|
1045
|
+
offset: 240,
|
|
1046
|
+
});
|
|
844
1047
|
|
|
845
1048
|
await act(async () => {
|
|
846
1049
|
props.onScroll?.({
|
|
@@ -851,12 +1054,12 @@ describe("ThreadMessages", () => {
|
|
|
851
1054
|
},
|
|
852
1055
|
});
|
|
853
1056
|
});
|
|
854
|
-
h.
|
|
1057
|
+
h.scrollToOffset.mockClear();
|
|
855
1058
|
await act(async () => {
|
|
856
1059
|
props.onContentSizeChange?.(0, 380);
|
|
857
1060
|
});
|
|
858
1061
|
|
|
859
|
-
expect(h.
|
|
1062
|
+
expect(h.scrollToOffset).not.toHaveBeenCalled();
|
|
860
1063
|
});
|
|
861
1064
|
});
|
|
862
1065
|
});
|