Repository navigation
[fix] Modal: make a modal active when it shows, not when its animation ends - #2904
Open
ilazaridis wants to merge 1 commit into
Open
ilazaridis wants to merge 1 commit into
ilazaridis wants to merge 1 commit into
Conversation
…n ends A modal joined the stack of active modals in onShow, which an animated modal calls once its animation ends. Until then the modal below stayed active: it kept role="dialog", handled Escape, and its focus trap moved any focus outside it, including focus moving inside the new modal, back to its own first focusable element. A Tab, a tap, or a programmatic focus inside a modal opening over another went to the modal below. A modal now joins the stack in a layout effect when it becomes visible. Layout effects run children first, so a modal rendered inside another and shown in the same commit is added first; a context gives each modal the modal it is rendered in, and a modal is inserted below the modals inside it, so the inner one stays on top. onShow still fires once the animation ends.
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit bed61f7:
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A modal joins the stack of active modals in
onShow. An animated modal callsonShowonly when its animation ends, 250 ms after it shows. Until then the modal below stays active:role="dialog";That last point includes focus moving inside the new modal. So with a modal opening over another, focus can't move inside the new one until it has finished animating:
focus()on mount (for example, focusing a search field) does the same.Then, once the new modal becomes active, its own trap finds focus outside it and moves it to its first focusable element.
We hit this with a picker sheet (
animationType="fade") opened from a filter sheet: its search field couldn't take focus when it opened, and a Tab right after it opened went to the filter sheet.Change
A modal joins the stack in a layout effect when it becomes visible (through
modules/useLayoutEffect, so SSR still usesuseEffect), not inonShow.focus()queued on mount (in a microtask) lands inside the new, active modal.onShowstill fires when the animation ends.Layout effects run children first. So a modal rendered inside another, with both shown in the same commit, would join the stack first, and the outer one would end up on top. A context gives each modal the id of the modal it is rendered in, and a modal is inserted below the modals inside it. The existing test
escape key fires onRequestClose for top modal only with animationcovers this case. It fails without the context and passes with it.Tests
New tests:
a modal is active before its animation endsfocus moved into a modal opening over another stays thereescape goes to a modal opening over another before its animation endsa modal shown inside another in the same commit stays on topAll four fail on master and pass with the change. The existing Modal tests pass unchanged.
npm run flow: no errors.npm run formatandnpm run lintpass.jest --config ./configs/jest.config{,.node}.js --runInBand).We run the same change as a patch on 0.21.3, with React 19.2, together with #2879. Our end-to-end suite passes with it in Chromium and WebKit. It covers nested sheets, focus trapping, Tab cycling, Escape, focus return and drag-to-close. A test that holds the upper modal's animation fails without the change: the modal below stays the dialog, and a Tab goes to it.
This is independent of #2879 (trap and Escape as layout effects), and either can land first.