v0.142.0
Minor Changes
-
#1188
dd3419ceThanks @tenphi! - Breaking: Refs forwarded toModal,Tray,Dialog,Form,MenuTrigger,Menu,CommandMenu,RadioGroup,CheckboxGroup, andLabelnow resolve to the underlying DOM element directly. The previous@react-spectrum/utils{ UNSAFE_getDOMNode() }wrapper (DOMRefValue) has been removed. Migrate by readingref.currentinstead of callingref.current?.UNSAFE_getDOMNode()on these components.Internally, these components now use
useObjectReffrom@react-aria/utilsin place ofuseDOMReffrom@react-spectrum/utils. Refs into focusable wrappers likeButton(which still useuseFocusableRef) are unaffected and continue to exposeUNSAFE_getDOMNode()plusfocus(). -
#1188
dd3419ceThanks @tenphi! - New: Pressing aButtonorItemButtoninside an open popover now
dismisses that popover by default. This covers every overlay that uses
usePopoverSync—FilterPicker,Picker,ComboBox,Select,
MenuTrigger,SubMenuTrigger,DialogTrigger type="popover",
use-anchored-menu, anduse-context-menu. Modal/tray/fullscreen Dialogs
are not affected — a Button inside a modal Dialog still does not
auto-close it.The dismiss event is dispatched through the EventBus and deferred via
setTimeout(0), so synchronousonPresshandlers (and any React state
updates they trigger) flush BEFORE the popover closes. This makes the
common "open a hoisted modal from a popover footer" flow work without
flicker: the modal mounts first, then the popover closes.Opt-outs
-
Automatic — Buttons that act as popover triggers (
MenuTrigger
children,DialogTrigger type="popover"children, picker trigger
ItemButtons) already carrydata-popover-triggerand skip the dismiss.
Modal-typeDialogTriggerchildren stay unmarked so they correctly
dismiss the parent popover and let the modal take over. -
Manual — Add
data-popover-keepto a Button (toggle case) or any
ancestor element (subtree case):<Button data-popover-keep onPress={() => setMode((m) => !m)}> Toggle </Button> <div data-popover-keep>{/* every Button inside opts out */}</div>
-
Custom non-Cube triggers — call the newly exported
useDismissParentPopover()to wire the same behaviour into your own
interactive controls.
Migration
Existing Buttons / ItemButtons inside popovers that were expected to keep
the popover open on press (toggles, inline editors, mode switches, etc.)
needdata-popover-keep. Popover triggers wrapped byMenuTrigger,
DialogTrigger type="popover",FilterPicker,Picker,ComboBox, or
Selectneed no change — they're auto-skipped.Internals
usePopoverSyncgained adismissOnInnerButtonPressoption (default
true).DialogTriggerpassesfalsefor modal/tray/fullscreen types
so buttons inside those dialogs don't subscribe to the new dismiss
event. Thepopover:dismiss-ancestorEventBus event carries the
originating DOM element so each popover host can do acontainer.contains()
check before closing.usePopoverSyncgained acloseOnPeerOpenoption (defaulttrue).
DialogTriggerpassesfalsefor modal/tray/fullscreen/panel types so
a peer popover opening (e.g. viauseDialogContaineror
useAnchoredMenu) cannot bypass the dialog'sisDismissable/
onClosehandling and callstate.close()directly. The host still
EMITSpopover:open, so opening a modal correctly dismisses peer
popovers — only the listener side is gated.DialogTriggernow appliesdata-popover-triggerto its child press
responder only whentype === 'popover'. This is the critical
correctness piece for the original bug — when aDialogTrigger type="modal"
lives inside aFilterPickerfooter, pressing its trigger now correctly
dismisses the FilterPicker, and the modal takes over.EventBusProvideris now a no-op when nested inside another
EventBusProvider. The internalProviderfromprovider.tsxwraps
overlay content with its ownEventBusProvider, which used to silently
shadow the global<Root>bus and prevent cross-overlay events from
reaching the host.usePopoverSync's nested-popover guard now walks the logical
popover chain via a module-level registry of open overlays, rather than
relying solely on a directcontainer.contains(triggerEl)DOM check.
Popover content is portaled to a shared overlay root, so a grandchild
popover's trigger lives in a sibling portal — not inside its
grandparent's DOM. Without the chain walk, opening a third+ level
SubMenuTrigger(or any equivalent nested popover) closed every
ancestor.
-
Patch Changes
- #1189
cb9c0635Thanks @solarrust! -ListBox: fix missing bottom spacing for the last item inisReorderablemode.