Purpose
A directional scroll must not deliver a swipe outside the scrollable it resolved. Endpoints are currently viewport-centred with a span of amount x viewport height and are never clamped, so on an inset scrollable the drag can start or end in the navigation bar and the gesture lands on the wrong surface.
This issue originally reported two defects. The disclosure half — a scroll that observed nothing answering with the distance it was asked for — has landed; what remains is the geometry half below.
Status: what already landed
On main at 913e22545 (layers #2770, #2776, #2771, #2772; unreleased at the time of writing, 0.21.12 being the newest published version):
- A directional scroll answers with observed
movement (moved | at-edge | unchanged | unobserved) instead of the requested distance, and a scroll that made no progress while the container still reports hidden content fails with details.reason === 'scroll_no_progress', carrying containerRect, hiddenContentAt, and requestedPixels.
moved is confined to the resolved scroller, so an unrelated on-screen change cannot be credited to the gesture. Measured on one AVD: a status-bar battery icon changing alone used to produce movement: 'moved' attempts:1; on the fixed build the same experiment returns unobserved with change-outside-container while the app's own row geometry is identical across the gesture.
- The defect below is now observable rather than silent: when the resolved container does not hold the swipe midpoint, the answer is
movement: 'unobserved' with reason container-outside-swipe and a containerRect (src/daemon/scroll-movement.ts:465). That detects the condition; it does not correct the gesture.
Remaining defect: unclamped geometry
Gesture endpoints are centred on the viewport with a span of amount x viewport height, and are never clamped to the resolved scrollable:
| amount |
gesture |
viewport |
| 0.5 |
656 -> 218 |
402x874 |
| 0.6 |
699 -> 175 |
|
| 0.75 |
765 -> 109 |
|
The /automation scrollable is inset from the window:
ScrollView / role=RCTEnhancedScrollView subrole=UIScrollView rect=(18,178,366,662)
so a 0.75 gesture ends at y=109, which is 69pt above the scrollable's own top edge, in the navigation bar. On /catalog the same build reports rect=(18,62,366,729), every amount stays inside it, and scrolling works in both directions.
Required behavior
- When a scroll container is resolved, both directional swipe endpoints are computed inside its
containerRect.
- Where the container can carry the requested span, the requested direction and amount are preserved exactly.
- Where it cannot, the dispatched span is reduced to the container and the answer states the span actually dispatched, rather than silently repeating the requested one.
container-outside-swipe ceases to be reachable for an inset-but-adequate scrollable, because the gesture no longer leaves the container.
Observable completion conditions
- Regression coverage for a container inset from the viewport: the emitted gesture endpoints lie inside
containerRect, and the reported distance is the span actually dispatched.
- Regression coverage for a container that already fits the span: endpoints and reported distance are unchanged from today's behavior.
- An inset route of the shape above moves content (
movement: 'moved') where it previously returned unobserved / container-outside-swipe.
Open question before this is AFK-ready
When the resolved container is shorter than the span the caller asked for: scale the span down to the container, or refuse with a typed reason? Both are defensible. Either way requestedPixels must stay distinguishable from the span actually dispatched, so the caller can tell a reduced gesture from a full one.
Environment and provenance
Reported against the SDK 58 fixture's /automation route; run 35523907559, job 106112615304. That build cannot launch on the 27.x runtimes available here, so reproduction on this host is the tester app's inset surfaces together with the geometry table above.
Purpose
A directional
scrollmust not deliver a swipe outside the scrollable it resolved. Endpoints are currently viewport-centred with a span ofamount x viewport heightand are never clamped, so on an inset scrollable the drag can start or end in the navigation bar and the gesture lands on the wrong surface.This issue originally reported two defects. The disclosure half — a scroll that observed nothing answering with the distance it was asked for — has landed; what remains is the geometry half below.
Status: what already landed
On
mainat913e22545(layers #2770, #2776, #2771, #2772; unreleased at the time of writing,0.21.12being the newest published version):movement(moved|at-edge|unchanged|unobserved) instead of the requested distance, and a scroll that made no progress while the container still reports hidden content fails withdetails.reason === 'scroll_no_progress', carryingcontainerRect,hiddenContentAt, andrequestedPixels.movedis confined to the resolved scroller, so an unrelated on-screen change cannot be credited to the gesture. Measured on one AVD: a status-bar battery icon changing alone used to producemovement: 'moved' attempts:1; on the fixed build the same experiment returnsunobservedwithchange-outside-containerwhile the app's own row geometry is identical across the gesture.movement: 'unobserved'with reasoncontainer-outside-swipeand acontainerRect(src/daemon/scroll-movement.ts:465). That detects the condition; it does not correct the gesture.Remaining defect: unclamped geometry
Gesture endpoints are centred on the viewport with a span of
amount x viewport height, and are never clamped to the resolved scrollable:656 -> 218699 -> 175765 -> 109The
/automationscrollable is inset from the window:so a
0.75gesture ends aty=109, which is 69pt above the scrollable's own top edge, in the navigation bar. On/catalogthe same build reportsrect=(18,62,366,729), every amount stays inside it, and scrolling works in both directions.Required behavior
containerRect.container-outside-swipeceases to be reachable for an inset-but-adequate scrollable, because the gesture no longer leaves the container.Observable completion conditions
containerRect, and the reported distance is the span actually dispatched.movement: 'moved') where it previously returnedunobserved/container-outside-swipe.Open question before this is AFK-ready
When the resolved container is shorter than the span the caller asked for: scale the span down to the container, or refuse with a typed reason? Both are defensible. Either way
requestedPixelsmust stay distinguishable from the span actually dispatched, so the caller can tell a reduced gesture from a full one.Environment and provenance
Reported against the SDK 58 fixture's
/automationroute;run 35523907559, job106112615304. That build cannot launch on the 27.x runtimes available here, so reproduction on this host is the tester app's inset surfaces together with the geometry table above.