Skip to content

Commit 34482c8

Browse files
fix: introl-screen-native tests
1 parent b6be27f commit 34482c8

5 files changed

Lines changed: 46 additions & 27 deletions

File tree

packages/pluggableWidgets/intro-screen-native/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Fixed
10+
11+
- We fixed an issue where reopening a page containing the intro screen would show the first slide instead of the slide referenced by the active slide attribute, and would report a slide change that the user did not make.
12+
913
## [4.4.1] - 2026-6-10
1014

1115
### Changed

packages/pluggableWidgets/intro-screen-native/e2e/specs/maestro/IntroScreen.yaml

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,29 +39,17 @@ appId: "${APP_ID}"
3939
timeout: 5000
4040
- tapOn:
4141
text: "OK"
42-
# Wait for dialog to dismiss before navigating back
43-
- extendedWaitUntil:
44-
visible: "Go back"
45-
timeout: 3000
4642
- tapOn: "Go back"
47-
# Wait for widgets menu to appear before tapping intro screen again
48-
- extendedWaitUntil:
49-
visible: "Widgets menu"
50-
timeout: 5000
5143
- tapOn:
5244
text: "Intro screen"
5345
- extendedWaitUntil:
5446
visible: "Active slide: 2"
5547
timeout: 5000
5648
- tapOn:
5749
text: "NEXT"
58-
# Wait for slide transition to complete - longer timeout for Android emulator
5950
- extendedWaitUntil:
6051
visible: "Active slide: 3"
61-
timeout: 10000
62-
- extendedWaitUntil:
63-
visible: "FINISH"
64-
timeout: 10000
52+
timeout: 5000
6553
- tapOn:
6654
text: "FINISH"
6755
- extendedWaitUntil:

packages/pluggableWidgets/intro-screen-native/src/SwipeableContainer.tsx

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,13 @@ const refreshActiveSlideAttribute = (slides: SlidesType[], activeSlide?: Editabl
6969
export const SwipeableContainer = (props: SwipeableContainerProps): ReactElement => {
7070
const [width, setWidth] = useState(0);
7171
const [height, setHeight] = useState(0);
72-
const [activeIndex, setActiveIndex] = useState(0);
72+
// Start on the slide the attribute points at, so activeIndex never lags the slide that
73+
// initialScrollIndex renders. Otherwise the first Next/Previous press moves relative to
74+
// slide 0 instead of the visible slide.
75+
const [activeIndex, setActiveIndex] = useState(() => refreshActiveSlideAttribute(props.slides, props.activeSlide));
7376
const flashList = useRef<FlashListRef<any>>(null);
74-
const isInitializing = useRef(true);
77+
const hasAppliedInitialScroll = useRef(false);
78+
const isUserDragging = useRef(false);
7579

7680
const rtlSafeIndex = useCallback(
7781
(i: number): number => (isAndroidRTL ? props.slides.length - 1 - i : i),
@@ -91,19 +95,18 @@ export const SwipeableContainer = (props: SwipeableContainerProps): ReactElement
9195
);
9296

9397
useEffect(() => {
98+
if (!width || props.activeSlide?.status !== ValueStatus.Available) {
99+
return;
100+
}
94101
const slide = refreshActiveSlideAttribute(props.slides, props.activeSlide);
95-
if (width && props.activeSlide?.status === ValueStatus.Available && slide !== activeIndex) {
102+
// Once width is known, force the list to the attribute's slide even when activeIndex
103+
// already matches: initialScrollIndex is applied before layout, so on remount the list
104+
// can sit at offset 0 while activeIndex says otherwise.
105+
if (!hasAppliedInitialScroll.current) {
106+
hasAppliedInitialScroll.current = true;
107+
goToSlide(slide);
108+
} else if (slide !== activeIndex) {
96109
goToSlide(slide);
97-
if (isInitializing.current) {
98-
if (isInitializing.current) {
99-
// Use requestAnimationFrame twice to wait for the next frame after scroll.
100-
requestAnimationFrame(() => {
101-
requestAnimationFrame(() => {
102-
isInitializing.current = false;
103-
});
104-
});
105-
}
106-
}
107110
}
108111
}, [props.activeSlide, activeIndex, width, props.slides, goToSlide]);
109112

@@ -315,15 +318,28 @@ export const SwipeableContainer = (props: SwipeableContainerProps): ReactElement
315318
);
316319
};
317320

321+
const onScrollBeginDrag = useCallback(() => {
322+
isUserDragging.current = true;
323+
}, []);
324+
318325
const onMomentumScrollEnd = useCallback(
319326
(event: NativeSyntheticEvent<any>) => {
327+
const wasUserDragging = isUserDragging.current;
328+
isUserDragging.current = false;
329+
330+
if (!width) {
331+
return;
332+
}
320333
const offset = event.nativeEvent.contentOffset.x;
321334
const newIndex = rtlSafeIndex(Math.round(offset / width));
322335
if (newIndex === activeIndex) {
323336
return;
324337
}
325338

326-
if (isInitializing.current) {
339+
// Only a user swipe reports a change here. Programmatic scrolls (initial positioning,
340+
// Next/Previous/pagination) already fired onSlideChange, so re-firing would double
341+
// count the change and, on remount, emit a spurious change back to slide 1.
342+
if (!wasUserDragging) {
327343
setActiveIndex(newIndex);
328344
return;
329345
}
@@ -366,6 +382,7 @@ export const SwipeableContainer = (props: SwipeableContainerProps): ReactElement
366382
bounces={false}
367383
style={styles.flatList}
368384
renderItem={renderItem}
385+
onScrollBeginDrag={onScrollBeginDrag}
369386
onMomentumScrollEnd={onMomentumScrollEnd}
370387
scrollEventThrottle={50}
371388
extraData={[width, activeIndex]}

packages/pluggableWidgets/intro-screen-native/src/__tests__/__snapshots__/IntroScreen.notch.spec.tsx.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ exports[`Intro Screen renders 1`] = `
5555
onLayout={[Function]}
5656
onMomentumScrollEnd={[Function]}
5757
onScroll={[Function]}
58+
onScrollBeginDrag={[Function]}
5859
pagingEnabled={true}
5960
scrollEventThrottle={50}
6061
showsHorizontalScrollIndicator={false}
@@ -281,6 +282,7 @@ exports[`Intro Screen renders with 1 bottom button 1`] = `
281282
onLayout={[Function]}
282283
onMomentumScrollEnd={[Function]}
283284
onScroll={[Function]}
285+
onScrollBeginDrag={[Function]}
284286
pagingEnabled={true}
285287
scrollEventThrottle={50}
286288
showsHorizontalScrollIndicator={false}
@@ -521,6 +523,7 @@ exports[`Intro Screen renders with 2 bottom button 1`] = `
521523
onLayout={[Function]}
522524
onMomentumScrollEnd={[Function]}
523525
onScroll={[Function]}
526+
onScrollBeginDrag={[Function]}
524527
pagingEnabled={true}
525528
scrollEventThrottle={50}
526529
showsHorizontalScrollIndicator={false}
@@ -761,6 +764,7 @@ exports[`Intro Screen renders with active slide attribute 1`] = `
761764
onLayout={[Function]}
762765
onMomentumScrollEnd={[Function]}
763766
onScroll={[Function]}
767+
onScrollBeginDrag={[Function]}
764768
pagingEnabled={true}
765769
scrollEventThrottle={50}
766770
showsHorizontalScrollIndicator={false}
@@ -987,6 +991,7 @@ exports[`Intro Screen renders with async storage identifier 1`] = `
987991
onLayout={[Function]}
988992
onMomentumScrollEnd={[Function]}
989993
onScroll={[Function]}
994+
onScrollBeginDrag={[Function]}
990995
pagingEnabled={true}
991996
scrollEventThrottle={50}
992997
showsHorizontalScrollIndicator={false}

packages/pluggableWidgets/intro-screen-native/src/__tests__/__snapshots__/IntroScreen.spec.tsx.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ exports[`Intro Screen renders 1`] = `
5555
onLayout={[Function]}
5656
onMomentumScrollEnd={[Function]}
5757
onScroll={[Function]}
58+
onScrollBeginDrag={[Function]}
5859
pagingEnabled={true}
5960
scrollEventThrottle={50}
6061
showsHorizontalScrollIndicator={false}
@@ -281,6 +282,7 @@ exports[`Intro Screen renders with 1 bottom button 1`] = `
281282
onLayout={[Function]}
282283
onMomentumScrollEnd={[Function]}
283284
onScroll={[Function]}
285+
onScrollBeginDrag={[Function]}
284286
pagingEnabled={true}
285287
scrollEventThrottle={50}
286288
showsHorizontalScrollIndicator={false}
@@ -521,6 +523,7 @@ exports[`Intro Screen renders with 2 bottom button 1`] = `
521523
onLayout={[Function]}
522524
onMomentumScrollEnd={[Function]}
523525
onScroll={[Function]}
526+
onScrollBeginDrag={[Function]}
524527
pagingEnabled={true}
525528
scrollEventThrottle={50}
526529
showsHorizontalScrollIndicator={false}
@@ -761,6 +764,7 @@ exports[`Intro Screen renders with active slide attribute 1`] = `
761764
onLayout={[Function]}
762765
onMomentumScrollEnd={[Function]}
763766
onScroll={[Function]}
767+
onScrollBeginDrag={[Function]}
764768
pagingEnabled={true}
765769
scrollEventThrottle={50}
766770
showsHorizontalScrollIndicator={false}
@@ -987,6 +991,7 @@ exports[`Intro Screen renders with async storage identifier 1`] = `
987991
onLayout={[Function]}
988992
onMomentumScrollEnd={[Function]}
989993
onScroll={[Function]}
994+
onScrollBeginDrag={[Function]}
990995
pagingEnabled={true}
991996
scrollEventThrottle={50}
992997
showsHorizontalScrollIndicator={false}

0 commit comments

Comments
 (0)