Skip to content

feat: Keyboard shortcut handler#9929

Merged
devongovett merged 29 commits into
mainfrom
keyboard-shortcut-handler
Jun 9, 2026
Merged

feat: Keyboard shortcut handler#9929
devongovett merged 29 commits into
mainfrom
keyboard-shortcut-handler

Conversation

@snowystinger

@snowystinger snowystinger commented Apr 15, 2026

Copy link
Copy Markdown
Member

Closes

Aiming to help us solve the issue of event leaks, this will allow us more fine grained control of what useKeyboard blocks/prevents default on in a more declarative way.

New behaviour is opt in through shortcuts in useKeyboard. This will preventDefault and stopPropagation if the associated function in the map returns true. Or it will let the event pass without altering it if false is returned. In addition, an object can be returned to alter that behaviour in whatever way necessary. Given that all key combinations are exclusive matches, this should make it more obvious what events are continuing or not.

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

One chain of events that was hard to track was submenutrigger Esc handling. Make sure to manually test this.

🧢 Your Project:

@github-actions github-actions Bot added the RAC label Apr 15, 2026
@snowystinger snowystinger changed the title Keyboard shortcut handler [WIP]: Keyboard shortcut handler Apr 15, 2026
@rspbot

rspbot commented Apr 30, 2026

Copy link
Copy Markdown

@github-actions github-actions Bot added the S2 label May 1, 2026
@rspbot

rspbot commented May 1, 2026

Copy link
Copy Markdown

@snowystinger snowystinger marked this pull request as ready for review May 1, 2026 06:37
@rspbot

rspbot commented May 1, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented May 1, 2026

Copy link
Copy Markdown


const isSelected = selectedKey === key;

let onKeyDown = (event) => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems just designed to skip arrow down and up, i think it'd be better to do this in the keyboard delegate or just allow it, i'm not sure why we wouldn't, there could be vertical steplists and it's a nice non-rtl dependent set of keys.

Just removing it for now as it'd make debugging difficult anyways

@rspbot

rspbot commented May 4, 2026

Copy link
Copy Markdown

@snowystinger snowystinger changed the title [WIP]: Keyboard shortcut handler feat: Keyboard shortcut handler May 4, 2026
@rspbot

rspbot commented May 22, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented May 26, 2026

Copy link
Copy Markdown

@github-actions github-actions Bot removed the RAC label May 29, 2026
@rspbot

rspbot commented May 29, 2026

Copy link
Copy Markdown

devongovett
devongovett previously approved these changes May 29, 2026
Comment thread packages/react-aria/src/actiongroup/useActionGroup.ts Outdated
state.setAnchorDate(null);
}
break;
return false; // TODO: is this really correct? or should it return true when we cancel and only propagate if there's nothing to do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return true inside the if statement? Was there a test that broke?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, react-spectrum/test/datepicker/DateRangePicker.test.js:439:28

'shift',
'alt',
'ctrl',
'control',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are control and ctrl different? should we standardize on one? I believe the standard e.key is Control

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, same thing, happy to standardize

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

though, just food for thought, it's e.ctrlKey, not e.controlKey

Comment thread packages/react-aria/src/interactions/useKeyboard.ts Outdated
Comment thread packages/react-aria/src/menu/useMenuItem.ts Outdated
Comment thread packages/react-aria/src/overlays/useOverlay.ts Outdated
Comment thread packages/react-aria/src/searchfield/useSearchField.ts Outdated

const isSelected = selectedKey === key;

let onKeyDown = event => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why my comment is marked as changed/obsolete #9929 (comment)

@rspbot

rspbot commented May 31, 2026

Copy link
Copy Markdown

devongovett
devongovett previously approved these changes Jun 1, 2026
break;
case 'Escape':
// TODO: can remove this when we fix collection event leaks
// TODO is this one covered by a test? did my changes to have a default stop on portals break this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically, need to test this one, manually to double check. Since unit tests are potentially inaccurate on this due to events having explicit targets and properties set by us. Make sure to open submenus in RAC and S2 and close multi level's with Esc. I didn't find any differences, but I'll move this to the description instead as part of testing.

@snowystinger

snowystinger commented Jun 3, 2026

Copy link
Copy Markdown
Member Author

Note to self, props.onKeyDown should be called first, otherwise it's only being called sometimes

Nevermind, it's only being skipped if it's through a portal, is a repeat that's not allowed or is a composing event that isn't allowed at the moment. The rest of the time it's called. So we can debate this some more.

# Conflicts:
#	packages/react-aria/src/combobox/useComboBox.ts
#	packages/react-aria/src/selection/useSelectableCollection.ts
@rspbot

rspbot commented Jun 9, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 9, 2026

Copy link
Copy Markdown

@rspbot

rspbot commented Jun 9, 2026

Copy link
Copy Markdown
## API Changes

@react-aria/interactions

/@react-aria/interactions:KeyboardProps

 KeyboardProps {
+  allowComposing?: boolean
+  allowRepeats?: boolean
   isDisabled?: boolean
   onKeyDown?: (KeyboardEvent) => void
   onKeyUp?: (KeyboardEvent) => void
+  shortcuts?: KeyboardShortcutBindings
 }

@reidbarber reidbarber left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

@devongovett devongovett added this pull request to the merge queue Jun 9, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 9, 2026
@devongovett devongovett added this pull request to the merge queue Jun 9, 2026
Merged via the queue into main with commit 9e1b070 Jun 9, 2026
31 checks passed
@devongovett devongovett deleted the keyboard-shortcut-handler branch June 9, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants