diff --git a/e2e/tests/issues/issue-1202-blank-page.e2e.ts b/e2e/tests/issues/issue-1202-blank-page.e2e.ts new file mode 100644 index 00000000..ca92c78e --- /dev/null +++ b/e2e/tests/issues/issue-1202-blank-page.e2e.ts @@ -0,0 +1,40 @@ +import { freshAppBeforeEach, expect, test } from '../support/test.ts'; +import type { Screen } from '../support/test.ts'; +import { openExample } from '../support/app.ts'; + +freshAppBeforeEach(); + +/** Focuses the input on `page`, types into it, then pages with Go `next`. */ +async function typeThenGo( + screen: Screen, + page: number, + next: number +): Promise { + const input = screen.getByTestId(`issue-1202-input-${page}`); + await input.tap(); + await input.pressSequentially(`${page}${page}${page}${page}`); + await screen.getByTestId(`issue-1202-go-${next}`).tap(); + await expect(screen.getByTestId(`issue-1202-label-${next}`)).toBeVisible(); +} + +test( + 'a page shown by setPage is not blank', + // iOS-only: the blank page comes from SwiftUI hosting a page twice. + { tags: ['regression', 'issue-1202'], platforms: ['ios'] }, + async ({ app, screen }) => { + await openExample(app, screen, 'Issue #1202 Blank Page Repro'); + + await typeThenGo(screen, 0, 1); + await typeThenGo(screen, 1, 2); + await typeThenGo(screen, 2, 1); + await typeThenGo(screen, 1, 0); + await typeThenGo(screen, 0, 1); + + // Before the fix page 2 was selected here but showed nothing. + await screen.getByTestId('issue-1202-go-2').tap(); + await expect(screen.getByTestId('issue-1202-status')).toHaveText( + /^Selected: 2 \|/ + ); + await expect(screen.getByTestId('issue-1202-label-2')).toBeVisible(); + } +); diff --git a/example/src/App.tsx b/example/src/App.tsx index 92f5d899..ccdebfd7 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -60,6 +60,7 @@ import { } from './gh-issues/Issue1142SearchBarInsetRepro'; import { Issue1154PageChangeFocusRepro } from './gh-issues/Issue1154PageChangeFocusRepro'; import { Issue1156NestedHorizontalScrollRepro } from './gh-issues/Issue1156NestedHorizontalScrollRepro'; +import { Issue1202BlankPageRepro } from './gh-issues/Issue1202BlankPageRepro'; import { Issue424SwipeCancelsPressRepro } from './gh-issues/Issue424SwipeCancelsPressRepro'; import { Issue965PagesBeforeCurrentRepro } from './gh-issues/Issue965PagesBeforeCurrentRepro'; import { colors, radius } from './theme'; @@ -188,6 +189,10 @@ const sections: { title: string; data: Example[] }[] = [ component: Issue1156NestedHorizontalScrollRepro, name: 'Issue #1156 Nested Horizontal Scroll Repro', }, + { + component: Issue1202BlankPageRepro, + name: 'Issue #1202 Blank Page Repro', + }, { component: Issue424SwipeCancelsPressRepro, name: 'Issue #424 Swipe Cancels Press Repro', diff --git a/example/src/gh-issues/Issue1202BlankPageRepro.tsx b/example/src/gh-issues/Issue1202BlankPageRepro.tsx new file mode 100644 index 00000000..12d9945a --- /dev/null +++ b/example/src/gh-issues/Issue1202BlankPageRepro.tsx @@ -0,0 +1,142 @@ +/** + * Repro for #1202: on iOS, a page shown by setPage must not be blank. The + * event log above the pager is cleared by each Go button and grows on every + * pager, focus and keyboard event, so the pager is laid out again around each + * page change. Focusing and typing on several pages and then paging with the + * Go buttons used to leave the selected page empty. + */ +import React, { useEffect, useRef, useState } from 'react'; +import { + Button, + Keyboard, + StyleSheet, + Text, + TextInput, + View, +} from 'react-native'; +import PagerView from 'react-native-pager-view'; + +const PAGES = ['Page 0', 'Page 1', 'Page 2']; +const PAGE_COLORS = ['#fde2e2', '#e2fde6', '#e2e8fd']; + +export function Issue1202BlankPageRepro() { + const pagerRef = useRef(null); + const [selectedPage, setSelectedPage] = useState(0); + const [focusedInput, setFocusedInput] = useState(null); + const [events, setEvents] = useState([]); + const log = (event: string) => + setEvents((current) => [...current.slice(-11), event]); + + useEffect(() => { + const show = Keyboard.addListener('keyboardDidShow', () => { + log('keyboardShow'); + }); + const hide = Keyboard.addListener('keyboardDidHide', () => { + log('keyboardHide'); + }); + return () => { + show.remove(); + hide.remove(); + }; + }, []); + + return ( + + + Selected: {selectedPage} | Focused:{' '} + {focusedInput === null ? 'none' : `input ${focusedInput}`} + + + Events: {events.join(', ')} + + + {PAGES.map((label, i) => ( +