Skip to content

ColumnFilterMenu Escape dismissal does not restore focus to its trigger #241

Description

@woksin

What happens

In @cratis/components 4.1.0, keyboard activation of a ColumnFilterMenu trigger opens the portalled filter dialog and focus moves into its value control. Pressing Escape closes the overlay, but document.activeElement becomes BODY instead of the exact filter trigger that opened it.

This path begins with an actual trigger activation; it is not an initially-open dialog with no invoker.

Evidence

The evidence is bound to exact tag v4.1.0 (c4c4cc9a13c6416eb3ab31701546e961b4e9da72):

  • ColumnFilterMenu.tsx composes the controlled DialogTrigger, AriaButton trigger, portalled Popover, and AriaDialog; Components owns the complete interaction boundary.
  • A current consumer browser run records that Escape closes the filter overlay and the active element afterward is BODY.
  • Exact-tag ColumnFilterMenu specs cover labels, stable parts, custom elements, localization, provider messages, and state attributes, but do not dismiss with Escape and assert focus equality with the invoker.
  • Package metadata advertises focus.restore, while CONFORMANCE.md bounds its checks and does not instrument vendor focus traps or dismissal listeners; the observed action/result therefore remains unguarded.

What it costs a consumer

The consumer supplies ordinary column field/header/filter/placeholder props and no custom focus or Escape behavior. Keyboard users lose context after dismissing a filter and must navigate back to the table controls. A consumer requestAnimationFrame focus patch or wrapper ref would duplicate package ownership, depend on dismissal timing, and leave other DataPage consumers exposed.

Suggested fix — the seam

Restore focus inside ColumnFilterMenu's package-owned trigger/composition boundary when Escape dismisses the overlay. Add a package-owned real-browser regression that activates the exact trigger, moves focus to the value control, presses Escape, and retry-asserts focus equality with that trigger, both before and after applying and reopening a constraint.

What is explicitly not being asked for

  • No consumer post-close focus patch, timeout, or wrapper trigger ref.
  • No prescribed internal mechanism: current evidence does not settle whether controlled state, ref forwarding, React Aria composition, or another detail causes it.
  • No redesign of every Dialog or Popover from this one ColumnFilterMenu defect.
  • No change to filtering, localization, or match-mode semantics.

Related, not duplicate

#211 is broad cross-browser and assistive-technology certification work. It is related, but it does not describe or settle this atomic ColumnFilterMenu Escape-to-trigger defect.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions