EPMRPP-117731 || 'Caps Lock' icon is displayed in the password fields even when the Caps Lock is off#313
Conversation
…con only when the field is focused
WalkthroughThe Caps Lock hook now synchronizes with the OS modifier state, rechecks after CapsLock key release, and resets when the tab becomes visible. ChangesCaps Lock behavior
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: dependency version conflict. Check your lock file or package.json. 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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/common/hooks/useCapsLock.ts (1)
24-60: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winConsolidate event listeners for scalability and add
mousedownsync.The current implementation effectively addresses the synchronization requirements. However, since
FieldTextis a base component that utilizes this hook, rendering multiple inputs on a single page will attach duplicate sets ofwindowanddocumentevent listeners, causing them all to fire on every keystroke.Consider extracting the listener logic to the module level using React 18's
useSyncExternalStore. This maintains a single global subscription regardless of how many inputs are rendered. Additionally, listening tomousedownevents allows the state to sync immediately when a user clicks the page or input, without waiting for their first keypress.♻️ Proposed refactor using `useSyncExternalStore`
Replace the hook implementation with a module-level store:
import { useSyncExternalStore } from 'react'; let globalCapsLockOn = false; let isListening = false; const listeners = new Set<() => void>(); const emitChange = () => listeners.forEach((listener) => listener()); const syncCapsLock = (event: Event) => { const modifierEvent = event as KeyboardEvent | MouseEvent; const newState = modifierEvent.getModifierState?.('CapsLock') ?? false; if (newState !== globalCapsLockOn) { globalCapsLockOn = newState; emitChange(); } }; const handleKeyUp = (event: KeyboardEvent) => { // CapsLock keydown fires before the OS toggles the modifier; keyup reads the real state. if (event.key === 'CapsLock') { syncCapsLock(event); } }; // Caps Lock may be toggled in another browser tab where this page receives no // keyboard events. Reset on tab return; the real state is re-detected on the // next keydown or mousedown while a password field is focused. const handleVisibilityChange = () => { if (document.visibilityState === 'visible' && globalCapsLockOn) { globalCapsLockOn = false; emitChange(); } }; const subscribe = (listener: () => void) => { if (!isListening && typeof window !== 'undefined') { window.addEventListener('keydown', syncCapsLock); window.addEventListener('keyup', handleKeyUp); window.addEventListener('mousedown', syncCapsLock); document.addEventListener('visibilitychange', handleVisibilityChange); isListening = true; } listeners.add(listener); return () => { listeners.delete(listener); }; }; const getSnapshot = () => globalCapsLockOn; const getServerSnapshot = () => false; export const useCapsLock = () => { const capsLockOn = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); return { capsLockOn }; };🤖 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/common/hooks/useCapsLock.ts` around lines 24 - 60, Refactor useCapsLock to use a module-level external store with useSyncExternalStore, keeping one shared set of window/document listeners regardless of how many hook instances render. Preserve the existing keydown, CapsLock keyup, and visibility-reset behavior, add mousedown synchronization, and ensure subscriptions are cleaned up and SSR uses a false server snapshot.
🤖 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.
Nitpick comments:
In `@src/common/hooks/useCapsLock.ts`:
- Around line 24-60: Refactor useCapsLock to use a module-level external store
with useSyncExternalStore, keeping one shared set of window/document listeners
regardless of how many hook instances render. Preserve the existing keydown,
CapsLock keyup, and visibility-reset behavior, add mousedown synchronization,
and ensure subscriptions are cleaned up and SSR uses a false server snapshot.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6d562957-437b-42f5-83ba-2629b823d51f
📒 Files selected for processing (1)
src/common/hooks/useCapsLock.ts
Description
checking the visibility and resetting the caps lock state to off, after returning from another tab
Visibility
visibilitycheck.mp4
Summary by CodeRabbit