Skip to content

refactor(hotkeys): migrate Hotkeys from Flow to TypeScript - #4769

Open
bonchevskyi wants to merge 1 commit into
box:masterfrom
bonchevskyi:refactor/flow-to-ts-hotkeys
Open

refactor(hotkeys): migrate Hotkeys from Flow to TypeScript#4769
bonchevskyi wants to merge 1 commit into
box:masterfrom
bonchevskyi:refactor/flow-to-ts-hotkeys

Conversation

@bonchevskyi

@bonchevskyi bonchevskyi commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Convert Hotkeys component to TypeScript

This PR converts src/components/hotkeys from JavaScript with Flow to TypeScript.

Changes

  • Converted hotkeys modules to .ts/.tsx with exported props interfaces (HotkeysProps, HotkeyLayerProps, HotkeyHelpModalProps, HotkeyFriendlyModalProps, HotkeyFriendlyOverlayProps) and HotkeyConfig
  • Converted index.js to index.ts, re-exporting components and types (runtime exports unchanged; no new parent barrel export)
  • Converted Hotkeys.stories.js to Hotkeys.stories.tsx
  • Converted __tests__/*.test.js to .test.ts(x)
  • Created .js.flow stubs for backward compatibility
  • Added @types/mousetrap for TypeScript support

Contract

  • Declared Flow props contract preserved (requiredness, accepted values, defaults, exports)
  • HotkeyFriendlyModalProps: Flow declared only children + isOpen; TS also includes className, onRequestClose, and title used by in-repo callers and forwarded via ...rest (previously allowed by Flow inexact objects)

Testing

  • Ran tests for src/components/hotkeys; all 50 pass with snapshots matching previous output
  • yarn lint:ts and flow check pass
  • Manually verified in Storybook (Components/Hotkeys) that behavior is unchanged

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features
    • Added configurable keyboard shortcut support across the application.
    • Added layered shortcut handling to prevent conflicts between active dialogs and overlays.
    • Added a localized shortcut help modal with categorized shortcuts and formatted key combinations.
    • Added shortcut-aware modal and overlay components.
  • Documentation
    • Added localized labels for common keyboard keys and shortcut sequences.
  • Tests
    • Updated shortcut-related tests for improved TypeScript compatibility.

@bonchevskyi
bonchevskyi requested a review from a team as a code owner August 11, 2026 12:45
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fc41af29-d79e-4e19-a1a3-b67dda9d28b1

📥 Commits

Reviewing files that changed from the base of the PR and between e41851e and d33096e.

📒 Files selected for processing (3)
  • src/components/hotkeys/HotkeyHelpModal.tsx
  • src/components/hotkeys/HotkeyLayer.tsx
  • src/components/hotkeys/__tests__/HotkeyTestWrapper.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/hotkeys/tests/HotkeyTestWrapper.tsx
  • src/components/hotkeys/HotkeyHelpModal.tsx

Walkthrough

The PR adds a layered hotkey system with Mousetrap integration, React context, registration components, hotkey-aware modal and overlay wrappers, a localized help modal, TypeScript and Flow modules, public exports, and updated tests.

Changes

Hotkey service foundation

Layer / File(s) Summary
Contracts and layered service
package.json, src/components/hotkeys/HotkeyContext.*, src/components/hotkeys/HotkeyManager.*, src/components/hotkeys/HotkeyRecord.*, src/components/hotkeys/HotkeyService.*, src/components/hotkeys/__tests__/HotkeyManager.test.ts, src/components/hotkeys/__tests__/HotkeyService.test.ts
Defines hotkey records and context. Manages active layers, keyboard listeners, registration, deregistration, duplicate detection, grouping, and cleanup.

React hotkey integration

Layer / File(s) Summary
Layer, registration, and UI wrappers
src/components/hotkeys/HotkeyLayer.*, src/components/hotkeys/Hotkeys.*, src/components/hotkeys/HotkeyFriendlyModal.*, src/components/hotkeys/HotkeyFriendlyOverlay.*
Provides HotkeyService through context, registers configured shortcuts, manages the help-modal shortcut, and wraps modal and overlay components with HotkeyLayer.
Exports and TypeScript test support
src/components/hotkeys/index.*, src/components/hotkeys/__tests__/HotkeyTestWrapper.tsx, src/components/hotkeys/__tests__/*, src/components/hotkeys/Hotkeys.stories.tsx
Exports hotkey modules and public types. Adds the TypeScript test wrapper and updates test typing and Enzyme assertions.

Localized hotkey help

Layer / File(s) Summary
Help modal and localization
src/components/hotkeys/HotkeyHelpModal.*, src/components/hotkeys/messages.*, src/components/hotkeys/__tests__/HotkeyHelpModal.test.tsx
Loads active hotkeys from context, formats alternatives and sequences, translates special keys, supports category selection, and renders the localized modal.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HotkeyLayer
  participant HotkeyService
  participant Hotkeys
  participant HotkeyContext
  participant HotkeyHelpModal
  participant Modal
  HotkeyLayer->>HotkeyService: create and register layer
  HotkeyLayer->>HotkeyContext: provide service
  Hotkeys->>HotkeyContext: read service
  Hotkeys->>HotkeyService: register configured hotkeys
  HotkeyHelpModal->>HotkeyContext: read active hotkeys and types
  HotkeyHelpModal->>Modal: render localized categories and bindings
Loading

Possibly related PRs

Suggested reviewers: jpan-box, tjuanitas

Poem

I tap my paws on keys so bright,
Layers catch the chords just right.
A modal lists each secret tune,
With translated keys beneath the moon.
The rabbit ships the shortcuts soon.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description clearly states the migration scope, contract considerations, compatibility approach, testing, and verification results.
Title check ✅ Passed The title clearly and concisely identifies the migration of the Hotkeys component from Flow to TypeScript.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​types/​mousetrap@​1.6.151001006980100

View full report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
src/components/hotkeys/HotkeyLayer.js.flow (1)

1-11: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

**Inconsistent // @flow pragma across the new `.js.flow` stubs.** `HotkeyFriendlyModal.js.flow` starts with `// `@flow, but two other new stubs do not. Use one convention for all stubs added in this PR.

  • src/components/hotkeys/HotkeyLayer.js.flow#L1-L11: add // @flow`` as the first line, or remove it from HotkeyFriendlyModal.js.flow.
  • src/components/hotkeys/Hotkeys.js.flow#L1-L64: apply the same convention to this stub.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/hotkeys/HotkeyLayer.js.flow` around lines 1 - 11, Use a
consistent Flow pragma convention across the new stubs: add `// `@flow`` as the
first line of `src/components/hotkeys/HotkeyLayer.js.flow` and
`src/components/hotkeys/Hotkeys.js.flow`, matching
`HotkeyFriendlyModal.js.flow`.
src/components/hotkeys/__tests__/Hotkeys.test.tsx (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Narrow the mock casts instead of disabling the rule for the whole file.

A file-wide no-explicit-any disable also hides future any usage in this test. The provider value accepts HotkeyService | null, so a double cast keeps the mock typed at the use site.

♻️ Proposed change
-/* eslint-disable `@typescript-eslint/no-explicit-any` */
-                <HotkeyContext.Provider value={mockHotkeyLayer as any}>
+                <HotkeyContext.Provider value={mockHotkeyLayer as unknown as HotkeyService}>

wrapper.instance() as any on Line 107 still needs a cast, because Enzyme returns the base Component type. Cast it to the component type instead: wrapper.instance() as Hotkeys.

Also applies to: 27-27

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/hotkeys/__tests__/Hotkeys.test.tsx` at line 1, Remove the
file-wide no-explicit-any suppression and narrow the mock casts at the provider
value use sites by casting through unknown to HotkeyService | null. Update each
wrapper.instance() cast in the Hotkeys tests to the Hotkeys component type
instead of any, preserving the existing test behavior.
src/components/hotkeys/HotkeyFriendlyModal.tsx (1)

18-28: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use event-aware callback types.

Modal passes an event to both callbacks. Type onBackdropClick as (event: React.MouseEvent<HTMLDivElement>) => void and onRequestClose as (event: React.SyntheticEvent) => void. Replace Object with React.CSSProperties.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/hotkeys/HotkeyFriendlyModal.tsx` around lines 18 - 28, Update
the HotkeyFriendlyModal props: type onBackdropClick as (event:
React.MouseEvent<HTMLDivElement>) => void, onRequestClose as (event:
React.SyntheticEvent) => void, and replace the style backdrop/dialog Object
types with React.CSSProperties.
src/components/hotkeys/Hotkeys.ts (1)

18-20: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Declare the context field as type-only.

The current es5 target does not emit this uninitialized field. Use declare to preserve that intent if the compiler target changes. React 18 typings do not infer the instance context type from static contextType.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/hotkeys/Hotkeys.ts` around lines 18 - 20, Update the context
field in the Hotkeys class to use a type-only declaration with the existing
HotkeyService | null type, while preserving static contextType = HotkeyContext
so React receives the runtime context.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/hotkeys/HotkeyHelpModal.tsx`:
- Around line 87-97: Update the reopen logic in both
src/components/hotkeys/HotkeyHelpModal.tsx (lines 87-97) and
src/components/hotkeys/HotkeyHelpModal.js.flow (lines 68-78): after refreshing
hotkeys and types in the isOpen transition, retain currentType only when it
exists in the refreshed types, otherwise select the first type or null. Add a
test covering active types changing while the modal is closed and reopening.

In `@src/components/hotkeys/HotkeyLayer.tsx`:
- Around line 21-24: Update the exported HotkeyLayerProps interface so
enableHelpModal and helpModalShortcut are optional, matching the existing
defaultProps and non-required PropTypes contract; leave their types and default
behavior unchanged.
- Around line 9-10: Restore the import/no-cycle suppression for the Hotkeys and
HotkeyHelpModal imports in HotkeyLayer.tsx, preserving the existing lint
configuration style and suppressing only the reported TypeScript hotkey cycle.

---

Nitpick comments:
In `@src/components/hotkeys/__tests__/Hotkeys.test.tsx`:
- Line 1: Remove the file-wide no-explicit-any suppression and narrow the mock
casts at the provider value use sites by casting through unknown to
HotkeyService | null. Update each wrapper.instance() cast in the Hotkeys tests
to the Hotkeys component type instead of any, preserving the existing test
behavior.

In `@src/components/hotkeys/HotkeyFriendlyModal.tsx`:
- Around line 18-28: Update the HotkeyFriendlyModal props: type onBackdropClick
as (event: React.MouseEvent<HTMLDivElement>) => void, onRequestClose as (event:
React.SyntheticEvent) => void, and replace the style backdrop/dialog Object
types with React.CSSProperties.

In `@src/components/hotkeys/HotkeyLayer.js.flow`:
- Around line 1-11: Use a consistent Flow pragma convention across the new
stubs: add `// `@flow`` as the first line of
`src/components/hotkeys/HotkeyLayer.js.flow` and
`src/components/hotkeys/Hotkeys.js.flow`, matching
`HotkeyFriendlyModal.js.flow`.

In `@src/components/hotkeys/Hotkeys.ts`:
- Around line 18-20: Update the context field in the Hotkeys class to use a
type-only declaration with the existing HotkeyService | null type, while
preserving static contextType = HotkeyContext so React receives the runtime
context.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 08265f37-0e24-470d-a8ce-b3343767c347

📥 Commits

Reviewing files that changed from the base of the PR and between 5d273fe and d563b32.

⛔ Files ignored due to path filters (2)
  • src/components/hotkeys/__tests__/__snapshots__/HotkeyFriendlyOverlay.test.tsx.snap is excluded by !**/*.snap
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (33)
  • package.json
  • src/components/hotkeys/HotkeyContext.js.flow
  • src/components/hotkeys/HotkeyContext.ts
  • src/components/hotkeys/HotkeyFriendlyModal.js.flow
  • src/components/hotkeys/HotkeyFriendlyModal.tsx
  • src/components/hotkeys/HotkeyFriendlyOverlay.js.flow
  • src/components/hotkeys/HotkeyFriendlyOverlay.tsx
  • src/components/hotkeys/HotkeyHelpModal.js.flow
  • src/components/hotkeys/HotkeyHelpModal.tsx
  • src/components/hotkeys/HotkeyLayer.js.flow
  • src/components/hotkeys/HotkeyLayer.tsx
  • src/components/hotkeys/HotkeyManager.js.flow
  • src/components/hotkeys/HotkeyManager.ts
  • src/components/hotkeys/HotkeyRecord.js.flow
  • src/components/hotkeys/HotkeyRecord.ts
  • src/components/hotkeys/HotkeyService.js.flow
  • src/components/hotkeys/HotkeyService.ts
  • src/components/hotkeys/Hotkeys.js.flow
  • src/components/hotkeys/Hotkeys.stories.tsx
  • src/components/hotkeys/Hotkeys.ts
  • src/components/hotkeys/__tests__/HotkeyFriendlyModal.test.tsx
  • src/components/hotkeys/__tests__/HotkeyFriendlyOverlay.test.tsx
  • src/components/hotkeys/__tests__/HotkeyHelpModal.test.tsx
  • src/components/hotkeys/__tests__/HotkeyLayer.test.tsx
  • src/components/hotkeys/__tests__/HotkeyManager.test.ts
  • src/components/hotkeys/__tests__/HotkeyService.test.ts
  • src/components/hotkeys/__tests__/HotkeyTestWrapper.js
  • src/components/hotkeys/__tests__/HotkeyTestWrapper.tsx
  • src/components/hotkeys/__tests__/Hotkeys.test.tsx
  • src/components/hotkeys/index.js.flow
  • src/components/hotkeys/index.ts
  • src/components/hotkeys/messages.js.flow
  • src/components/hotkeys/messages.ts
💤 Files with no reviewable changes (1)
  • src/components/hotkeys/tests/HotkeyTestWrapper.js

Comment thread src/components/hotkeys/HotkeyHelpModal.tsx
Comment thread src/components/hotkeys/HotkeyLayer.tsx
Comment thread src/components/hotkeys/HotkeyLayer.tsx Outdated
@bonchevskyi
bonchevskyi force-pushed the refactor/flow-to-ts-hotkeys branch from d563b32 to e41851e Compare August 11, 2026 13:22
@bonchevskyi
bonchevskyi force-pushed the refactor/flow-to-ts-hotkeys branch from e41851e to d33096e Compare August 11, 2026 14:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant