Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
**/.xcode.env
**/.xcode.env.local

# Android/IJ
Expand Down Expand Up @@ -82,3 +83,4 @@ example/ios/PagerViewExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
!.yarn/releases
!.yarn/sdks
!.yarn/versions
.agent-device
58 changes: 58 additions & 0 deletions .maestro/issues/issue_1083_modal_set_page_repro.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
appId: com.pagerviewexample
tags:
- ios
- regression
---
- runFlow: ../setup/issue_1083_modal_set_page_repro_setup.yaml

# This deliberately invokes setPage while the pager is obscured by a native
# stack modal. Before the fix, React state became 0 while SwiftUI showed Page 1.
- tapOn:
id: 'issue-1083-open-modal'

- extendedWaitUntil:
visible: 'Modal screen'
timeout: 10000

- tapOn:
id: 'issue-1083-submit'

- extendedWaitUntil:
visible: 'Last requested page: 1'
timeout: 10000

- extendedWaitUntil:
visible:
id: 'issue-1083-page-1'
timeout: 10000

- tapOn:
id: 'issue-1083-advance-directly'

- extendedWaitUntil:
visible: 'Last requested page: 2'
timeout: 10000

- extendedWaitUntil:
visible:
id: 'issue-1083-page-2'
timeout: 10000

- tapOn:
id: 'issue-1083-open-modal'

- extendedWaitUntil:
visible: 'Modal screen'
timeout: 10000

- tapOn:
id: 'issue-1083-submit'

- extendedWaitUntil:
visible: 'Last requested page: 0'
timeout: 10000

- extendedWaitUntil:
visible:
id: 'issue-1083-page-0'
timeout: 10000
20 changes: 20 additions & 0 deletions .maestro/setup/issue_1083_modal_set_page_repro_setup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
appId: com.pagerviewexample
---
- launchApp

- scrollUntilVisible:
element:
id: 'Issue #1083 Modal SetPage Repro'
direction: DOWN

- tapOn:
id: 'Issue #1083 Modal SetPage Repro'

- extendedWaitUntil:
visible:
id: 'issue-1083-requested-page'
timeout: 10000

- assertVisible: 'Last requested page: 0'
- assertVisible:
id: 'issue-1083-page-0'
3 changes: 2 additions & 1 deletion .maestro/tests/material_top_bar_example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ appId: com.pagerviewexample
id: 'material-top-bar-detail-screen'
timeout: 5000

- pressKey: Back
- tapOn:
id: 'material-top-bar-back-button'

- extendedWaitUntil:
visible:
Expand Down
1 change: 1 addition & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.gradle/
.idea/
.vs/
**/.xcode.env
**/.xcode.env.local
Pods/
build/
Expand Down
11 changes: 0 additions & 11 deletions example/ios/.xcode.env

This file was deleted.

4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1402,7 +1402,7 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- react-native-pager-view (9.0.0):
- react-native-pager-view (9.0.3):
- hermes-engine
- RCTRequired
- RCTTypeSafety
Expand Down Expand Up @@ -2496,7 +2496,7 @@ SPEC CHECKSUMS:
React-Mapbuffer: 1aa9126122d4247ffc24bf9d28d50ce923499a71
React-microtasksnativemodule: d86581169e9bb5bb6f5fc3c5052f890016c1bf21
React-mutationobservernativemodule: 9a0c4e866f1ef2a57acebc902ebacbdf469ae741
react-native-pager-view: 91a6d37f552b9c47445cc94792ad75cfa08487b2
react-native-pager-view: 27f9cb535d59648492018892b7e3bffe0b511807
react-native-safe-area-context: c1eb308f4b36372a4de4b3bdaa8ed695ec3dd461
React-NativeModulesApple: cc6ec4767844d610e92cc358bd3ea34937438d56
React-networking: a8ce15641ed7775d5b54a9d0d32defc367c2216e
Expand Down
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"test": "jest",
"lint": "eslint .",
"mkdist": "node -e \"require('node:fs').mkdirSync('dist', { recursive: true, mode: 0o755 })\"",
"android": "react-native run-android --appId com.pagerviewexample --list-devices --active-arch-only",
"ios": "react-native run-ios --list-devices",
"android": "react-native run-android --appId com.pagerviewexample",
"ios": "react-native run-ios",
"visionos": "react-native run-visionos",
"build:android": "bun mkdist && react-native bundle --entry-file index.js --platform android --dev true --bundle-output dist/main.android.jsbundle --assets-dest dist/res",
"build:ios": "bun mkdist && react-native bundle --entry-file index.js --platform ios --dev false --bundle-output dist/main.ios.jsbundle --assets-dest dist",
Expand Down
20 changes: 18 additions & 2 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { PagerHookExample } from './PagerHookExample';
import { NestedHorizontalScrollViewExample } from './NestedHorizontalScrollViewExample';
import { Issue1098NestedPagerRepro } from './Issue1098NestedPagerRepro';
import { Issue1099SafeAreaRepro } from './Issue1099SafeAreaRepro';
import { Issue1098NestedPagerRepro } from './gh-issues/Issue1098NestedPagerRepro';
import { Issue1099SafeAreaRepro } from './gh-issues/Issue1099SafeAreaRepro';
import {
Issue1083ModalSetPageExample,
ModalSetPageModalScreen,
} from './gh-issues/Issue1083ModalSetPageExample';

function BasicPagerViewExampleScreen() {
return <BasicPagerViewExample isHorizontal={true} />;
Expand Down Expand Up @@ -108,6 +112,10 @@ const additionalExamples: Example[] = [
];

const ghIssues: Example[] = [
{
component: Issue1083ModalSetPageExample,
name: 'Issue #1083 Modal SetPage Repro',
},
{
component: Issue1098NestedPagerRepro,
name: 'Issue #1098 Nested Pager Repro',
Expand Down Expand Up @@ -249,6 +257,14 @@ export function Navigation() {
component={example.component}
/>
))}
<NavigationStack.Screen
name="ModalSetPageModal"
component={ModalSetPageModalScreen}
options={{
presentation: 'modal',
animation: 'slide_from_bottom',
}}
/>
</NavigationStack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
Expand Down
17 changes: 16 additions & 1 deletion example/src/MaterialTopTabExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@ const DetailScreen = () => (
</View>
);

const detailScreenOptions = ({ navigation }: any) => ({
headerBackVisible: false,
headerLeft: () => (
<Button
testID="material-top-bar-back-button"
title="Back"
onPress={navigation.goBack}
/>
),
});

export function MaterialTopBarExample() {
const { Screen, Navigator } = createNativeStackNavigator();
const [isSignedIn, setIsSignedIn] = useState(false);
Expand All @@ -118,7 +129,11 @@ export function MaterialTopBarExample() {
)}
</Screen>
)}
<Screen name="Detail Screen" component={DetailScreen} />
<Screen
name="Detail Screen"
component={DetailScreen}
options={detailScreenOptions}
/>
</Navigator>
);
}
Expand Down
111 changes: 111 additions & 0 deletions example/src/gh-issues/Issue1083ModalSetPageExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Repro for #1083: setPage lands on the wrong index when called while the
* pager is obscured by a presented native-stack modal on iOS.
*/
import * as React from 'react';
import { Button, SafeAreaView, StyleSheet, Text, View } from 'react-native';
import {
useNavigation,
useRoute,
type RouteProp,
} from '@react-navigation/native';
import { type NativeStackNavigationProp } from '@react-navigation/native-stack';
import PagerView from 'react-native-pager-view';

export type ModalSetPageParamList = {
'Issue #1083 Modal SetPage Repro': undefined;
'ModalSetPageModal': { onSubmit: () => void };
};

const PAGES = ['Page 0', 'Page 1', 'Page 2'];
const PAGE_COLORS = ['#fde2e2', '#e2fde6', '#e2e8fd'];

export function Issue1083ModalSetPageExample() {
const pagerRef = React.useRef<PagerView>(null);
const [currentPage, setCurrentPage] = React.useState(0);
const navigation =
useNavigation<
NativeStackNavigationProp<
ModalSetPageParamList,
'Issue #1083 Modal SetPage Repro'
>
>();

const advancePager = React.useCallback(() => {
const next = (currentPage + 1) % PAGES.length;
console.log('[setPage]', next);
pagerRef.current?.setPage(next);
setCurrentPage(next);
}, [currentPage]);

const openModal = React.useCallback(() => {
navigation.navigate('ModalSetPageModal', { onSubmit: advancePager });
}, [advancePager, navigation]);

return (
<SafeAreaView style={styles.flex}>
<View style={styles.controls}>
<Text testID="issue-1083-requested-page" style={styles.label}>
Last requested page: {currentPage}
</Text>
<Button
testID="issue-1083-advance-directly"
title="Advance pager directly"
onPress={advancePager}
/>
<Button
testID="issue-1083-open-modal"
title="Open Modal"
onPress={openModal}
/>
</View>
<PagerView ref={pagerRef} style={styles.flex}>
{PAGES.map((label, i) => (
<View
key={label}
testID={`issue-1083-page-${i}`}
style={[styles.page, { backgroundColor: PAGE_COLORS[i] }]}
>
<Text style={styles.pageLabel}>{label}</Text>
</View>
))}
</PagerView>
</SafeAreaView>
);
}

export function ModalSetPageModalScreen() {
const navigation =
useNavigation<
NativeStackNavigationProp<ModalSetPageParamList, 'ModalSetPageModal'>
>();
const route =
useRoute<RouteProp<ModalSetPageParamList, 'ModalSetPageModal'>>();

const submit = React.useCallback(() => {
// Intentionally synchronous: update the hidden pager, then dismiss.
route.params.onSubmit();
navigation.popTo('Issue #1083 Modal SetPage Repro');
}, [navigation, route]);

return (
<SafeAreaView style={styles.flex}>
<View style={styles.controls}>
<Text style={styles.label}>Modal screen</Text>
<Button
testID="issue-1083-submit"
title="Submit & advance pager"
onPress={submit}
/>
</View>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
flex: { flex: 1 },
controls: { padding: 16, gap: 8 },
label: { fontSize: 14, color: '#444' },
page: { flex: 1, alignItems: 'center', justifyContent: 'center' },
pageLabel: { fontSize: 32, fontWeight: '600' },
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { ScrollView, StyleSheet, Text, View } from 'react-native';
import PagerView from 'react-native-pager-view';

import { IMAGE_URIS } from './utils';
import { IMAGE_URIS } from '../utils';

const CARD_HEIGHT = 500;

Expand Down
11 changes: 0 additions & 11 deletions example/visionos/.xcode.env

This file was deleted.

Loading
Loading