Skip to content

Release v4.11.2

Choose a tag to compare

@github-actions github-actions released this 22 Sep 08:12
166433d

Summary

ColumnFilterMenu composes React Aria's DialogTrigger + Popover + Dialog directly, with its own separate trigger button, rather than an integrated single-owner control like ComboBox/Dropdown. React Aria's built-in focus restoration for that shape only notices focus fell to document.body on a requestAnimationFrame-deferred check, so any observer sampling focus right after a dismissal (a keyboard user, a screen reader, or an automated check) sees focus land on the document instead of the trigger. This happened identically whether the popover was dismissed with Escape or by clicking outside; Tab does not dismiss the popover at all, since React Aria already traps focus inside it while open.

The restore is deliberately conditional. React Aria's own restore is guarded — at teardown it asks whether the active element is still inside the dying scope or has already fallen to document.body, and leaves focus alone otherwise, so it only ever recovers focus that was dropped, never focus the user moved. Only the timing of that check is wrong for this shape, not the check itself. ColumnFilterMenu therefore keeps React Aria's question and drops its delay: the same predicate is evaluated synchronously in the effect cleanup that runs in the commit closing the menu, with nothing deferred left to lose a race with.

Read at that moment, "dropped" cannot simply mean document.body: the popover is still exiting and still in the document, so focus is usually still on the popover's own value input — a drop in progress, not a destination. A drop is therefore nothing focused, the body, a node already detached from the document, or anything inside this menu's own popover. Focus anywhere else is somewhere the user deliberately went, such as another column's filter trigger, and is left untouched.

Fixed

  • ColumnFilterMenu now returns focus to its own trigger button when the filter popover closes having dropped focus, for both Escape and outside-click dismissal, instead of depending on React Aria's timing-sensitive automatic restore (#241)
  • Dismissing a filter menu by moving focus elsewhere — for example pressing another column's filter trigger — leaves focus where the user put it, rather than pulling it back to the trigger whose menu closed (#241)

Behavior and compatibility

No rendered DOM and no CSS changed. Serialized markup for the closed, open and reclosed states is identical to pre-PR main apart from React Aria's own data-focused / data-focus-visible attributes following the restored focus, which is the behavior this PR exists to produce.