Sheet: an enter frame at the viewport's edge leaves the sheet never entered, and a Select pressed during the rise opens an inert list
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 38/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- react, typescript
- Ambito
- accessibility, frontend
Direzione di ricerca
Read SheetOverlay and the IntersectionObserver setup in the sheet implementation, then trace how UNSTABLE_deferUntilEntered invokes ariaHideOutside in useModalOverlay. Reproduce both timing cases from the issue and inspect the existing sheet and overlay tests. Done means both defects have focused fixes and tests: edge-adjacent entry is recognized without breaking exit, and a Select opened during entry remains interactive.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
These are two separate defects in SheetOverlay. Both happen while a sheet slides in, and I found them together, but they have different causes and different fixes. If you would rather track them apart, please say so and I will split this.
Versions: react-aria-components 1.22.1 and react-aria 3.53.1, the latest releases. The expressions in defect 1 are unchanged on main as of 7 November 2026.
Browsers: Playwright 1.64.1's builds of Chromium 156 and WebKit, on Windows 11, with Playwright's iPhone 17 profile. I have not tested a real iPhone or Linux.
Related, and different: #10813 and #10897 lose the last report of an exit. #10877 is a scroll position written during an exit. Defect 1 is about the enter, and every report arrives. #10814 reports part of defect 2 for a ComboBox.
Defect 1: a frame exactly at the viewport's edge leaves the sheet never entered
What goes wrong
SheetOverlay observes the content with threshold: [0, 1] and reads entry.intersectionRatio > 0:
isVisible.current = entry.intersectionRatio > 0;
hasEntered ||= entry.intersectionRatio > 0;
if (hasEntered && entry.intersectionRatio <= 0) {
If a frame of the slide lands exactly where the content's leading edge touches the viewport's edge, the intersection has no area. The IntersectionObserver specification counts an edge-adjacent target as intersecting, so the observer reports isIntersecting: true with intersectionRatio: 0.
The next report then comes only when the threshold index changes. Ratio 0 while intersecting and any ratio between 0 and 1 have the same index, since the first threshold greater than either is 1. So nothing else is reported until the content is whole or gone.
A sheet that rests with all of its content visible recovers at ratio 1. A sheet that rests below ratio 1 does not, and hasEntered and isVisible.current stay false while it stands open on screen. Two ordinary shapes rest below 1:
- a partial detent from
snapPoints, by reading the code; I did not test this shape; - content partly clipped by the sheet. Ours has a drag handle above
SheetContent, which pushes the content past the sheet's clipped bottom, so it rests at ratio 0.875 or 0.9375 (measured).
What the person sees
- Desktop Safari: closing the sheet strands it.
onExitseesisVisible.currentfalse and waits for ascrollend, but nothing scrolls, so the overlay stays on the page withdata-exiting. This was measured in WebKit withnavigator.platformleft asWin32. - iOS, with
navigator.platformset toiPhonein WebKit: the close button still works. A swipe down, simulated with an instant scroll, leaves the sheet off-screen and open:hasEnteredis false, so the ratio 0 report closes nothing, and the page stays inert until one tap anywhere closes it.
How often
The rate depends on where the frames land.
- Without any forcing, 3 of 325 rounds stranded in Windows WebKit, in a loop that opened a sheet, picked a value in it and closed it.
- Two of those three were caught with logging. Each log shows two reports at ratio 0, then
scrollendat the rest position, then no further report. The first report is the initial one, off screen. A second report at the same ratio can only come fromisIntersectingchanging, which is what identifies the edge frame.
main looks likely to make this much more frequent, by reading only; I have not run it. skipOffscreenTravel from #10815, which is not in 1.22.1, moves the scroller so that "the sheet's leading edge sits at the viewport edge" before the smooth scroll starts one frame later. That is this frame, placed there on purpose. If the jump lands exactly, every sheet resting below ratio 1 would stay un-entered on every open.
Reproduction
- Render a bottom
Sheetwhose content rests below ratio 1, for example with an element aboveSheetContentandoverflow: clipon theSheet. - Before opening it, wrap
Element.prototype.scrollToso that the first smooth call on[data-sheet-scroll]does this instead: turn snapping off, setscrollToptocontent.getBoundingClientRect().top + scroller.scrollTop - window.innerHeight, wait 120 ms, restore snapping, and then make the original call. This forces the frame instead of waiting for one. - Wrap
IntersectionObserverto logintersectionRatioandisIntersectingfor[data-sheet-content]. - Open the sheet. It rises and rests on screen. The log's last entry has ratio 0.
- In WebKit, close it with a button, or scroll
[data-sheet-scroll]to 0. See the two results above.
Possible fix
These are two candidates. Neither has run against your test suite.
- Read
entry.isIntersectingin those three expressions. We are patching 1.22.1 this way. It has one catch that does not affect us: a sheet as tall as the viewport rests at scroll 0 with its content exactly edge-adjacent, soisIntersectingwould stay true at the exit position andreact-aria-sheet-closewould never fire. Our sheets are at most 92% of the viewport with a handle above the content, so they stop short of that. - Add a small threshold, for example
threshold: [0, 0.001, 1], and keep the ratio checks. The ratio leaving 0 then crosses a threshold and produces a report, and the exit check keeps working at the edge. I have not tested this one.Number.MIN_VALUEmay not work, because browsers may store thresholds in lower precision.
The second looks safer to me for the general case, but you know the sheet's geometry better than I do.
Defect 2: a Select pressed while the sheet rises opens a list that goes inert
What goes wrong
SheetOverlay uses UNSTABLE_deferUntilEntered, so useModalOverlay calls ariaHideOutside([modal], {shouldUseInert: true}) only once the sheet has entered. The sheet's controls can already be pressed during the slide.
- A press on a
Selectinside the sheet opens its popover, portaled outside the modal. The popover's ownariaHideOutside([popover])runs. - The slide ends, and the modal's deferred hide runs. Its
visibleNodesholds only the modal, so the walk marks the popover's portal containerinert. The popover opened first, so nothing registered it with this later hide.
The order is reversed from what the hide expects: the popover is above the modal, but the modal's hide runs after it.
What the person sees
The list is on screen and looks normal, but it ignores presses. A tap on an option closes the list with no choice made. A second try works, because the sheet has entered by then.
The window is about half a second after opening. A quick person can reach it.
Reproduction
- Render a bottom
Sheetcontaining aSelect. - Open the sheet, and press the Select's trigger while the overlay still has
data-entering. I pressed in the frame the scroller passed 620 of its 681 px of travel. - Wait for the slide to end. The popover's portal container or an ancestor is
inert. - Tap an option. The list closes and the value does not change.
This was measured in Chromium. I could not measure it in WebKit, so I do not know whether it behaves the same there.
Relation to #10814 and #10802
#10814 describes the same order of events for a ComboBox as its first part, with keepVisible registered with the combo box's own hide rather than the modal's. It was closed as a duplicate of #10802. As far as I can tell from their descriptions, #10802 and its pull requests #10812 and #10884 are about two hides sharing one element and restoring it correctly, which is the second part of #10814. The popover here is hidden by one call only, and it is the wrong one, so I do not think those fixes reach it. Please correct me if they do.
Possible fix
The deferred hide could keep visible whatever a hide opened during the deferral is keeping visible, since those overlays stand above the modal. Setting data-react-aria-top-layer on the popover also avoids it, but that attribute is an internal rather than an API, so I have not used it. I have not built the first option.
- Lingua principale
- TypeScript
- Stelle
- 16k
- Fork
- 1.6k
- Merge medio
- 4g 10h
- PR unite (30g)
- 42
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di adobe/react-spectrum
-
Declare @types/react as an optional peer dependencyForse già presa @Sophran-fbj l’ha presa 1 giorno fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
adobe/react-spectrum#10940 ·
I maintainer di solito rispondono entro 1 giorno
-
@internationalized/number: NumberParser misparses Arabic-digit numbers that use . , or ، (sd parses ١٬٢٣٤.٥ as 12345)Forse già presa @itsrayeedscodespace l’ha presa 4 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
adobe/react-spectrum#10936 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
PreviewTrigger: the preview dialog has no accessible nameForse già presa @therealemmanueloppong l’ha presa 6 giorni fa. Apertaaccessibility bug PreviewTrigger
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
adobe/react-spectrum#10917 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
S2 quiet Picker menu is always 192px wide: its width calc() has no unitForse di nuovo libera @darshpatel1052 l’ha presa 8 giorni fa e non c’è nessuna pull request aperta. Apertawaiting on author
Difficoltà 2/5 Meno di un'ora Idoneità per principianti 88/100
adobe/react-spectrum#10909 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
DatePicker omits timezone for initially empty fields despite zoned placeholderValueForse già presa @mrlexcoder l’ha presa 9 giorni fa. Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
adobe/react-spectrum#10900 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di adobe/react-spectrum
Issue simili
-
ログイン中の404画面にヘッダーとナビを出すApertaCodex使用可 コード
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
ryuki-moro/Nicomaru#218 ·
I maintainer di solito rispondono entro 3 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
sveltejs/svelte-eslint-parser#1008 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
silverbucket/inbox-rs#351 ·
I maintainer di solito rispondono entro 1 giorno
-
[Bug]: The H2 series' and the H3C's fan 3 is labelled "Chamber Fan", Bambu calls it the exhaust fanApertaarea:ui
Difficoltà 2/5 1-3 ore Idoneità per principianti 88/100
I maintainer di solito rispondono entro 1 giorno
-
bug front P2
Difficoltà 2/5 1-3 ore Idoneità per principianti 86/100
invoicerr-app/invoicerr#964 ·
I maintainer di solito rispondono entro 1 giorno