-
-
Notifications
You must be signed in to change notification settings - Fork 53
Open
Description
onKeyDown is overridden by consumer props and may override Floater’s handlers — compose instead
Spreading getFloatingProps() first, then defining onKeyDown, and finally spreading {...props} means a consumer-provided onKeyDown will replace your Escape/Tab logic; likewise, your handler may override any internal getFloatingProps().onKeyDown. This can break closing on Escape and focus trapping.
Refactor to compose handlers via getFloatingProps(userProps) and remove the trailing {...props}:
- {...getFloatingProps()}
+ {...getFloatingProps({
+ ...props,
+ onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => {
+ // Run consumer first
+ (props as any)?.onKeyDown?.(e);
+ if (e.defaultPrevented) return;
+ // Escape to close
+ if (e.key === 'Escape') {
+ e.preventDefault();
+ e.stopPropagation();
+ handleOpenChange(false);
+ return;
+ }
+ // Tab trap (see fix below for 0 focusable elements)
+ if (e.key === 'Tab') {
+ const focusableSelector = [
+ 'button:not([disabled])','[href]','input:not([disabled])',
+ 'select:not([disabled])','textarea:not([disabled])',
+ '[tabindex]:not([tabindex="-1"])'
+ ].join(',');
+ const focusableElements = Array.from(
+ contentRef.current?.querySelectorAll(focusableSelector) || []
+ ) as HTMLElement[];
+ if (focusableElements.length === 0) {
+ e.preventDefault();
+ (contentRef.current as HTMLElement | null)?.focus();
+ return;
+ }
+ const firstElement = focusableElements[0];
+ const lastElement = focusableElements[focusableElements.length - 1];
+ if (e.shiftKey) {
+ if (document.activeElement === firstElement) {
+ e.preventDefault();
+ lastElement.focus();
+ }
+ } else {
+ if (document.activeElement === lastElement) {
+ e.preventDefault();
+ firstElement.focus();
+ }
+ }
+ }
+ }
+ })}
- onKeyDown={(e) => {
- // Handle escape key
- if (e.key === 'Escape') {
- e.preventDefault();
- e.stopPropagation();
- handleOpenChange(false);
- }
- // Handle tab trapping
- if (e.key === 'Tab') {
- ...
- }
- }}
- {...props}Also applies to: 94-101, 136-138
🤖 Prompt for AI Agents
In src/core/primitives/Dialog/fragments/DialogPrimitiveContent.tsx around lines
86-87 (and likewise update lines 94-101 and 136-138), the current prop spread
order lets consumer onKeyDown override Floater handlers and vice-versa; fix by
passing the consumer props into getFloatingProps (e.g., call
getFloatingProps(props) / getFloatingProps(userProps)) instead of spreading
getFloatingProps() first and then {...props}, and remove the trailing {...props}
spread so event handlers are composed by getFloatingProps rather than
overwritten.
Originally posted by @coderabbitai[bot] in #1508 (comment)
Metadata
Metadata
Assignees
Labels
No labels