Skip to content

fix: keyboard shortcut hijack issues#3115

Merged
chronark merged 17 commits intomainfrom
fix-keyboard-hijack
Apr 25, 2025
Merged

fix: keyboard shortcut hijack issues#3115
chronark merged 17 commits intomainfrom
fix-keyboard-hijack

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Apr 15, 2025

What does this PR do?

Our current keyboard shortcuts were being hijacked by OS and Browser default shortcuts. So right now they mainly rely on option+shift+key combo and just simple letters like Status - S, Identity - I. The reason for option+shift combo is they are not used by major browsers.

Fixes # (issue)

If there is not an issue for this, please create one first. This is used to tracking purposes and also helps use understand why this PR exists

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • Test A
  • Test B

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features
    • Introduced a modular filter item component with improved accessibility and keyboard navigation in the logs filter popover.
  • Enhancements
    • Improved keyboard shortcut handling for filters, refresh, live updates, and navigation actions, now using Option+Shift key combinations for consistency.
    • Enhanced visual focus indicators for better accessibility.
    • Updated shortcut labels in the UI to reflect new key combinations.
    • Refined keyboard shortcut parsing and handling with more robust and configurable support.
    • Added comprehensive keyboard navigation and focus management within filter options and popovers.
    • Enabled keyboard navigation within filter operator inputs for better accessibility.
  • Bug Fixes
    • Prevented errors when displaying shortcut keys if none are set.
    • Fixed keyboard event propagation issues in filter checkboxes for improved interaction.
  • Refactor
    • Streamlined filter popover structure and focus management for a smoother user experience.
    • Simplified keyboard event handling in filter checkboxes and improved navigation logic.
    • Replaced deprecated responsive hook with a new mobile detection hook across multiple components.
    • Simplified mobile drawer UI and animation handling in the date-time filter popover.
    • Updated empty state layout and alignment for key verification logs.
    • Increased spacing between breadcrumb items in navigation for improved readability.
    • Removed unused responsive hook and related code.

@vercel
Copy link

vercel bot commented Apr 15, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
dashboard ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 24, 2025 2:43pm
play ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 24, 2025 2:43pm
www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Apr 24, 2025 2:43pm
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
engineering ⬜️ Ignored (Inspect) Visit Preview Apr 24, 2025 2:43pm

@changeset-bot
Copy link

changeset-bot bot commented Apr 15, 2025

⚠️ No Changeset found

Latest commit: 1b63e07

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 15, 2025

📝 Walkthrough
## Walkthrough

This set of changes updates keyboard shortcut handling and display across several dashboard components. The `useKeyboardShortcut` hook is refactored for improved parsing, configurability, and reliability, now supporting explicit key codes and a disabled state. Multiple components update their keyboard shortcut combinations, especially shifting from meta/ctrl-based shortcuts to option+shift-based ones, and update UI labels to match. The filter popover and filter item logic are modularized, with enhanced keyboard navigation, focus management, and shortcut registration. Visual adjustments are made to button focus rings, and shortcut display logic is made more robust. Additional keyboard accessibility improvements were made in filter-related components.

## Changes

| File(s)                                                                                       | Change Summary                                                                                                                                                                                                                                                                                                                                                                                        |
|-----------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| apps/dashboard/hooks/use-keyboard-shortcut.tsx                                                | Refactored hook for robust shortcut parsing, explicit key code matching, new `disabled` option, improved modifier handling, and better error handling. Updated signature and added supporting types/functions.                                                                                                                                                                                       |
| apps/dashboard/hooks/use-keyboard-shortcut.test.tsx                                          | Added comprehensive unit tests for `useKeyboardShortcut` hook and `parseShortcutString` utility, covering shortcut parsing, event handling, options, and edge cases.                                                                                                                                                                                                                               |
| apps/dashboard/components/logs/checkbox/filter-item.tsx                                       | Introduced new `FilterItem` React component for interactive filter items with popover UI, focus management, keyboard handling, and accessibility. Exported `FilterItem` and `FilterItemConfig` types.                                                                                                                                                                                               |
| apps/dashboard/components/logs/checkbox/filters-popover.tsx                                   | Refactored to use the new `FilterItem` component. Added `ShortcutActivator` for per-item shortcut registration. Enhanced keyboard navigation, focus management, and shortcut handling. Updated accessibility and UI.                                                                                                                                                                               |
| apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/index.tsx           | Changed filter item shortcut keys from lowercase to uppercase ("E", "M", "P"). Added `shortcutLabel` property to each filter item object.                                                                                                                                                                                                                                                         |
| apps/dashboard/components/keyboard-button.tsx                                                 | Added optional chaining to `shortcut?.toUpperCase()` to prevent errors when shortcut is falsy.                                                                                                                                                                                                                                          |
| apps/dashboard/components/logs/control-cloud/index.tsx                                        | Changed keyboard shortcuts for clearing/focusing filters from meta+key to option+shift+key. Updated displayed shortcut labels.                                                                                                                                                                                                         |
| apps/dashboard/components/logs/control-cloud/control-pill.tsx                                 | Updated Button focus ring styling: changed color class and added offset for visual adjustment.                                                                                                                                                                                                                                          |
| apps/dashboard/components/logs/live-switch-button/index.tsx                                   | Changed live toggle shortcut from meta+L to option+shift+L. Updated tooltip and displayed shortcut label. Added `KeyboardButton` for shortcut display.                                                                                                                                                                                  |
| apps/dashboard/components/logs/refresh-button/index.tsx                                       | Changed refresh shortcut from ctrl+R to option+shift+R. Updated hook usage, tooltip, and displayed shortcut label.                                                                                                                                                                                                                      |
| apps/dashboard/components/navbar-popover.tsx                                                  | Changed QuickNavPopover toggle shortcut from ctrl+key to option+shift+key. Updated shortcut display in header.                                                                                                                                                                                                                          |
| apps/dashboard/components/logs/checkbox/filter-checkbox.tsx                                   | Replaced keyboard event handler to stop propagation and manage focus explicitly on toggle keys. Wrapped click handlers to stop propagation. Added explicit IDs for checkbox inputs.                                                                                                                                                      |
| apps/dashboard/components/logs/filter-operator-input/index.tsx                                | Added keyboard navigation for filter options list with arrow keys, Enter/Space for selection, and Tab to move focus to textarea. Introduced refs for options and textarea.                                                                                                                                                               |
| apps/dashboard/components/logs/checkbox/hooks/index.ts                                       | Merged toggle logic into expanded `handleKeyDown`. Improved arrow key navigation scoped to local checkbox labels with wrap-around. Removed 'h','j','k','l' keys from navigation. Added error handling for focus.                                                                                                                                                                                  |
| apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/logs-table.tsx               | Adjusted empty state container and content alignment and sizing classes for key verification logs empty state UI.                                                                                                                                                                                                                       |
| apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx                                       | Replaced `useResponsive` hook with `useIsMobile` for mobile detection.                                                                                                                                                                                                                                                                    |
| apps/dashboard/components/logs/datetime/datetime-popover.tsx                                 | Replaced `useResponsive` with `useIsMobile`. Simplified mobile drawer UI by removing fixed height and nested overflow wrappers. Replaced conditional rendering with CSS-based show/hide toggling for time range suggestions and DateTime component. Updated toggle button styling.                                                                                                              |
| apps/dashboard/components/navigation/navbar.tsx                                              | Increased horizontal gap spacing between breadcrumb items from `gap-2` to `gap-3`.                                                                                                                                                                                                                                                       |
| apps/dashboard/components/ui/drover.tsx                                                     | Replaced `useResponsive` with `useIsMobile` for mobile detection in `Root` component.                                                                                                                                                                                                                                                    |
| apps/dashboard/components/virtual-table/index.tsx                                           | Replaced `useResponsive` with `useIsMobile` for mobile detection.                                                                                                                                                                                                                                                                         |
| apps/dashboard/hooks/use-responsive.tsx                                                     | Deleted `useResponsive` hook that tracked device type by window width and returned flags for mobile, tablet, desktop.                                                                                                                                                                                                                     |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant UI_Component
    participant useKeyboardShortcut
    participant Callback

    User->>UI_Component: Presses keyboard shortcut
    UI_Component->>useKeyboardShortcut: Registers shortcut with callback
    useKeyboardShortcut->>UI_Component: Listens for keydown events
    User->>UI_Component: Triggers keydown event
    UI_Component->>useKeyboardShortcut: Handles event, matches shortcut
    useKeyboardShortcut->>Callback: Invokes callback if match
    Callback->>UI_Component: Executes action (e.g., open popover, refresh)

Possibly related PRs

  • unkeyed/unkey#2804: Refactors filter logic including FilterItem component and props; related by shared components and shortcut handling.

Suggested labels

Dashboard

Suggested reviewers

  • chronark
  • mcstepp
  • perkinsjr


</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

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.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=unkeyed/unkey&utm_content=3115):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@github-actions
Copy link
Contributor

github-actions bot commented Apr 15, 2025

Thank you for following the naming conventions for pull request titles! 🙏

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (1)

33-134: Consider consolidating or refactoring the large mapping logic.
While the extensive switch/case in getKeyNameToCode is functional and explicit, you might consider consolidating mappings into a lookup table or employing a library to reduce verbosity and ease future maintenance.

apps/dashboard/components/keyboard-button.tsx (1)

31-31: Consider providing a fallback label when shortcut is undefined.
In cases where shortcut is falsy, displaying an empty <kbd> might be unclear to users. Including a short placeholder (e.g., "N/A") can improve usability.

Also applies to: 35-35

apps/dashboard/components/logs/checkbox/filter-item.tsx (2)

35-44: Solid synchronization of local and parent states.
Storing open locally but syncing it in a useEffect ensures external controls (via isActive) can override local state as needed. This pattern is appropriate for custom popover control, though pay attention to potential double-render if isActive changes frequently.


45-72: Thoughtful focus management.
Focusing the popover trigger or content based on isFocused and isActive fosters keyboard accessibility. The fallback focus on the container is also a nice touch. Verify ignoring the lint rule for missing dependencies won't cause stale references.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aac1c1b and 8a38b90.

📒 Files selected for processing (10)
  • apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/index.tsx (1 hunks)
  • apps/dashboard/components/keyboard-button.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filters-popover.tsx (3 hunks)
  • apps/dashboard/components/logs/control-cloud/control-pill.tsx (1 hunks)
  • apps/dashboard/components/logs/control-cloud/index.tsx (3 hunks)
  • apps/dashboard/components/logs/live-switch-button/index.tsx (3 hunks)
  • apps/dashboard/components/logs/refresh-button/index.tsx (2 hunks)
  • apps/dashboard/components/navbar-popover.tsx (2 hunks)
  • apps/dashboard/hooks/use-keyboard-shortcut.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (5)
apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/index.tsx (2)
apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/status-filter.tsx (1)
  • StatusFilter (41-70)
apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/components/methods-filter.tsx (1)
  • MethodsFilter (18-35)
apps/dashboard/components/logs/control-cloud/index.tsx (2)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (1)
  • useKeyboardShortcut (227-305)
apps/dashboard/components/keyboard-button.tsx (1)
  • KeyboardButton (11-38)
apps/dashboard/components/logs/refresh-button/index.tsx (3)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (1)
  • useKeyboardShortcut (227-305)
internal/icons/src/icons/refresh-3.tsx (1)
  • Refresh3 (15-38)
apps/dashboard/components/keyboard-button.tsx (1)
  • KeyboardButton (11-38)
apps/dashboard/components/logs/checkbox/filter-item.tsx (4)
apps/dashboard/components/logs/checkbox/filters-popover.tsx (1)
  • FilterItemConfig (15-21)
apps/dashboard/components/keyboard-button.tsx (1)
  • KeyboardButton (11-38)
internal/ui/src/components/button.tsx (1)
  • Button (443-443)
internal/icons/src/icons/caret-right.tsx (1)
  • CaretRight (15-37)
apps/dashboard/components/logs/checkbox/filters-popover.tsx (3)
apps/dashboard/components/logs/checkbox/filter-item.tsx (2)
  • FilterItemConfig (9-15)
  • FilterItem (24-184)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (1)
  • useKeyboardShortcut (227-305)
apps/dashboard/components/keyboard-button.tsx (1)
  • KeyboardButton (11-38)
⏰ Context from checks skipped due to timeout of 90000ms (14)
  • GitHub Check: Test Go API Local / Test (Shard 4/8)
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Go API Local / Test (Shard 7/8)
  • GitHub Check: Test Go API Local / Test (Shard 1/8)
  • GitHub Check: Test Go API Local / Test (Shard 5/8)
  • GitHub Check: Test Go API Local / Test (Shard 3/8)
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Go API Local / Test (Shard 2/8)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test Packages / Test ./internal/encryption
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (31)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (3)

1-11: Good introduction of type definitions and descriptive doc comments.
The KeyCombo interface comprehensively captures the relevant details (key, code, and modifiers). The accompanying doc comments clearly explain each property.


15-23: New 'disabled' option is well integrated.
Adding disabled?: boolean to the options provides a straightforward mechanism to turn off the listener, enhancing flexibility.


227-305: Well-implemented effect and matching logic.
The useKeyboardShortcut hook correctly handles typical use cases, including ignoring inputs/contentEditable and matching physical key codes. The code is clear, and merging user-provided options with defaults is concise.

apps/dashboard/app/(app)/logs/components/controls/components/logs-filters/index.tsx (1)

14-15: Updated shortcuts from lowercase to uppercase appear valid.
The change to "E," "M," and "P" aligns with the parser's logic, as it normalizes to lowercase internally. However, verify there is no overlap with the existing "Press 'F' to toggle filters" instruction (line 48) or other shortcuts.

Also applies to: 21-22, 28-29

apps/dashboard/components/keyboard-button.tsx (1)

31-31: Optional chaining prevents runtime errors.
Safely using shortcut?.toUpperCase() ensures no crash when shortcut is null or undefined. This is a clean and effective fix.

Also applies to: 35-35

apps/dashboard/components/logs/control-cloud/control-pill.tsx (1)

81-81: Improved focus state styling for better accessibility.

The focus ring styling has been enhanced with a better color contrast (focus:ring-accent-9 instead of the previous accent-7) and added offset (focus:ring-offset-1), which will improve visibility during keyboard navigation. This change complements the keyboard shortcut improvements in the PR.

apps/dashboard/components/navbar-popover.tsx (2)

70-72: Keyboard shortcut updated to use option+shift combination.

The shortcut has been updated from a Ctrl/Meta key combination to option+shift, addressing the PR objective of preventing conflicts with browser and OS default shortcuts.


229-229: Updated shortcut display to match new key combination.

The visual indicator of the keyboard shortcut has been updated to show the new option+shift combination with proper symbols (⌥+⇧), providing users with accurate visual cues.

apps/dashboard/components/logs/control-cloud/index.tsx (4)

27-43: Updated "clear filters" shortcut to use option+shift combination.

The keyboard shortcut for clearing filters has been changed from using meta/ctrl to option+shift+d, which aligns with the PR's goal of preventing conflicts with browser and OS shortcuts.


45-47: Updated "focus filters" shortcut to use option+shift combination.

The keyboard shortcut for focusing filters has been changed from using meta/ctrl to option+shift+c, which aligns with the PR's goal of preventing conflicts with browser and OS shortcuts.


147-147: Updated visual indicator for "clear filters" shortcut.

The keyboard shortcut indicator has been updated to show the new option+shift combination with proper symbols (⌥+⇧+D), providing users with accurate visual cues.


150-150: Updated visual indicator for "focus filters" shortcut.

The keyboard shortcut indicator has been updated to show the new option+shift combination with proper symbols (⌥+⇧+C), providing users with accurate visual cues.

apps/dashboard/components/logs/live-switch-button/index.tsx (4)

1-1: Added KeyboardButton component for visual shortcut display.

The KeyboardButton component is now imported to provide a visual representation of the keyboard shortcut, improving the user interface.


13-13: Updated live toggle shortcut to use option+shift combination.

The keyboard shortcut for toggling live updates has been changed from using meta+l to option+shift+l, which aligns with the PR's goal of preventing conflicts with browser and OS shortcuts.


20-20: Updated button tooltip text to reflect new shortcut combination.

The tooltip for the live toggle button has been updated to show the new option+shift key combination, ensuring consistent communication to users.


35-35: Added visual shortcut indicator to live toggle button.

A KeyboardButton component has been added to visually display the keyboard shortcut (⌥+⇧+L), making the shortcut more discoverable for users.

apps/dashboard/components/logs/refresh-button/index.tsx (2)

44-47: Looks good migrating to option+shift+r.
Ensuring disabled: !isEnabled prevents the shortcut from firing when refresh is unavailable, which aligns well with the overall logic of this PR.


68-68: Consistent with the assigned shortcut.
Using <KeyboardButton shortcut="⌥+⇧+R" /> cleanly displays the actual key combination.

apps/dashboard/components/logs/checkbox/filter-item.tsx (4)

24-34: Good separation of props for popover logic.
Defining FilterItemProps to extend the base config with isFocused, isActive, and a callback to set the active filter is a clear approach, promoting reusability.


74-91: Escape key handler logic is correct.
Stopping propagation prevents closing parent popovers and ensures focus management flows back up properly.


93-117: Graceful handling of outside clicks and open state changes.
Calling the parent callback if the popover closes unexpectedly (e.g., clicking outside) helps keep states in sync. The handleTriggerClick toggles activation cleanly.


119-185: Robust popover structure.
The PopoverTrigger and PopoverContent wrap is standard and consistent. Using pointer-events-none within nested elements ensures only the main trigger is clickable. Overall, the approach is well-structured and easy to maintain.

apps/dashboard/components/logs/checkbox/filters-popover.tsx (9)

29-49: Ingenious shortcut activation workaround.
Rendering the ShortcutActivator components so each item’s hook runs at the top level avoids manual listeners. This is slightly “hacky,” but a clean solution, given React’s hook rules.


55-57: Ensures filter count defaults to the active filters.
Providing a default getFilterCount avoids errors in scenarios where the function is not supplied.


62-63: Remembering the last focused index.
Tracking lastFocusedIndex is helpful for returning focus after closing a nested popover, enhancing consistent keyboard navigation.


79-91: Shortcut “f” toggles the main popover.
Preventing default stops browser find-in-page conflicts. Good approach for preventing hijacking.


93-106: Activating child filters from global shortcuts.
handleActivateFilter openly reuses the logic to set open and immediately focuses the correct item. This is a well-structured callback.


113-132: Conditional arrow handling.
Allowing arrow keys to exit from a child filter popover if it's active but ignoring them if an input is focused is thoughtful. This fosters more intuitive navigation.


135-170: Keyboard navigation in the main filter list.
Managing ArrowDown, ArrowUp, Enter, ArrowRight, and Escape for movement and activation seamlessly. This is consistent with typical menu or list controls.


175-186: Activators provide a central, consolidated approach.
Rendering these hidden components at the top ensures all shortcuts remain registered while simplifying the main UI code.


216-218: Enhanced filter header.
Labeling “Filter by...” clarifies the popover’s purpose. Displaying the “F” shortcut is consistent with the override logic.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (1)

136-215: Well-implemented parsing with thorough validation

The parseShortcutString function provides robust validation and normalized output. This is critical for the reliability of keyboard shortcuts across the application.

🧹 Nitpick comments (2)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (2)

33-134: Great implementation of comprehensive key mapping!

The getKeyNameToCode function provides an extensive and well-structured mapping from user-friendly key names to KeyboardEvent.code values, with excellent categorization for different key types.

For minor optimization, consider replacing the nested switch statements with a single mapping object:

export const getKeyNameToCode = (keyName: string): string | null => {
  const lowerKey = keyName.toLowerCase();

+  const keyMappings: Record<string, string> = {
+    // Letters and digits handled separately
+    
+    // Control keys
+    'enter': 'Enter',
+    'tab': 'Tab',
+    'space': 'Space',
+    'esc': 'Escape',
+    'escape': 'Escape',
+    'backspace': 'Backspace',
+    'delete': 'Delete',
+    
+    // Arrow keys
+    'up': 'ArrowUp',
+    'arrowup': 'ArrowUp',
+    // ...remaining mappings
+  };
+
+  // Handle letters
+  if (lowerKey.length === 1 && lowerKey >= "a" && lowerKey <= "z") {
+    return `Key${lowerKey.toUpperCase()}`;
+  }
+  
+  // Handle digits
+  if (lowerKey.length === 1 && lowerKey >= "0" && lowerKey <= "9") {
+    return `Digit${lowerKey}`;
+  }
+  
+  // Handle function keys
+  if (lowerKey.startsWith("f") && !Number.isNaN(Number.parseInt(lowerKey.substring(1), 10))) {
+    const fNum = Number.parseInt(lowerKey.substring(1), 10);
+    if (fNum >= 1 && fNum <= 12) {
+      return `F${fNum}`;
+    }
+  }
+  
+  // Look up from mapping
+  if (keyMappings[lowerKey]) {
+    return keyMappings[lowerKey];
+  }

  console.warn(
    `[useKeyboardShortcut] Could not map key name "${keyName}" to a standard KeyboardEvent.code...`
  );
  return null;
};

This would make the function more maintainable and potentially more efficient.


237-238: Add explanation for ignore directive

The biome-ignore directive is missing an explanation. Add a clear reason for why the dependency rule is being ignored here.

- // biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
+ // biome-ignore lint/correctness/useExhaustiveDependencies: callback is already listed as a dependency
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a38b90 and 1f07540.

📒 Files selected for processing (3)
  • apps/dashboard/components/logs/refresh-button/index.tsx (2 hunks)
  • apps/dashboard/hooks/use-keyboard-shortcut.test.tsx (1 hunks)
  • apps/dashboard/hooks/use-keyboard-shortcut.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/components/logs/refresh-button/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms (15)
  • GitHub Check: Test Go API Local / Test (Shard 6/8)
  • GitHub Check: Test Go API Local / Test (Shard 4/8)
  • GitHub Check: Test Go API Local / Test (Shard 2/8)
  • GitHub Check: Test Go API Local / Test (Shard 3/8)
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./internal/billing
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test ./internal/id
  • GitHub Check: autofix
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/dashboard/hooks/use-keyboard-shortcut.tsx (1)

281-297: Good upgrade to use code-based matching

Switching from e.key to e.code for matching is a significant improvement, as it provides more reliable behavior especially with modifier keys like Alt/Option which can produce different character outputs on different keyboard layouts.

apps/dashboard/hooks/use-keyboard-shortcut.test.tsx (3)

5-34: Well-designed test helper function

The dispatchKeyEvent helper function provides a clean way to simulate keyboard events with various modifier states, making the tests more readable and maintainable.


36-111: Comprehensive test coverage for basic functionality

These tests thoroughly verify the hook's behavior for various key combinations, ensuring that callbacks are triggered correctly and only when the exact key combination is pressed.


299-491: Excellent test coverage for parseShortcutString

This comprehensive set of tests addresses the previous review comment requesting test coverage for the parsing function. The tests cover normal cases, edge cases, error handling, and warn messages.

Copy link
Collaborator

@ogzhanolguncu k has a conflict in this menu
CleanShot 2025-04-17 at 09.47.03.mp4

Copy link
Collaborator

I don’t have a good suggestion though

Copy link
Collaborator

also it feels a little awkward that I can’t do arrows/j/k in this menu

maybe we should allow that and remap keys to something else?
CleanShot 2025-04-17 at 09.50.36@2x.png

Copy link
Collaborator

like I intuitively tried f k k and expected to be in the identity menu now, maybe that’s just me though

Copy link
Collaborator

how do I select any of these via keyboard?
I tried space and enter, neither worked
CleanShot 2025-04-17 at 09.52.15@2x.png

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

Code looks fine, but I found some usability issues mentioned in this thread

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

lgtm, but please fix the conflicts @ogzhanolguncu

Copy link
Contributor Author

sure

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
apps/dashboard/components/logs/checkbox/filters-popover.tsx (3)

100-112: Consider adding a comment explaining the setTimeout usage.

While the setTimeout with 0ms delay is a common pattern to defer execution to the next tick, adding a comment explaining why this is necessary would improve code clarity.

 useKeyboardShortcut(
   "f",
   () => {
     setOpen((prev) => {
       const newState = !prev;
       if (newState && items.length > 0) {
+        // Use setTimeout to ensure DOM is updated before setting focus
         setTimeout(() => setFocusedIndex(0), 0);
       }
       return newState;
     });
   },
   { preventDefault: true, ignoreInputs: true },
 );

114-127: Consider adding a comment for setTimeout in handleActivateFilter.

Similar to the previous comment, adding an explanation for the setTimeout usage would improve code clarity.

 const handleActivateFilter = useCallback(
   (id: string) => {
     setOpen(true);
+    // Use setTimeout to ensure the popover is rendered before setting focus
     setTimeout(() => {
       setActiveFilter(id);
       const index = items.findIndex((i) => i.id === id);
       if (index !== -1) {
         setFocusedIndex(index);
         setLastFocusedIndex(index);
       }
     }, 0);
   },
   [items, setOpen],
 );

156-191: Consider adding ARIA keyboard interactions for menu navigation.

While the keyboard navigation works well, consider enhancing accessibility by adding ARIA properties to better communicate the menu structure to screen readers. This aligns with the PR's focus on improving keyboard interactions.

 // In the JSX part (around line 220)
-<div className="flex flex-col gap-1 w-full p-2" role="menu">
+<div 
+  className="flex flex-col gap-1 w-full p-2" 
+  role="menu" 
+  aria-orientation="vertical"
+>
   {items.map((item, index) => (
     <FilterItem
       key={item.id}
       {...item}
       filterCount={getFilterCount(item.id)}
       isFocused={focusedIndex === index}
       isActive={activeFilter === item.id}
       setActiveFilter={setActiveFilter}
+      role="menuitem"
+      aria-selected={focusedIndex === index}
     />
   ))}
 </div>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee0e6f and 3434665.

📒 Files selected for processing (6)
  • apps/dashboard/components/keyboard-button.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filter-checkbox.tsx (3 hunks)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filters-popover.tsx (2 hunks)
  • apps/dashboard/components/logs/control-cloud/index.tsx (3 hunks)
  • apps/dashboard/components/logs/filter-operator-input/index.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/dashboard/components/logs/checkbox/filter-checkbox.tsx
  • apps/dashboard/components/keyboard-button.tsx
  • apps/dashboard/components/logs/filter-operator-input/index.tsx
  • apps/dashboard/components/logs/checkbox/filter-item.tsx
  • apps/dashboard/components/logs/control-cloud/index.tsx
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Test Packages / Test ./internal/keys
  • GitHub Check: Test Packages / Test ./internal/encryption
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/hash
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (10)
apps/dashboard/components/logs/checkbox/filters-popover.tsx (10)

21-21: Good addition of shortcutLabel to support display customization.

Adding the optional shortcutLabel property to the FilterItemConfig type allows for more flexibility in displaying shortcuts, helping to accommodate the new option+shift+key combinations mentioned in the PR objectives.


33-55: Smart use of a memoized null-rendering component for shortcut registration.

This is a clever pattern to handle dynamic shortcut registration. The component doesn't render anything but allows you to use React hooks for each filter item without manual event listener management. The comments clearly explain this "hacky" but effective workaround.


67-67: Improved focus management with lastFocusedIndex.

Adding the lastFocusedIndex state variable enhances the user experience by properly tracking and restoring focus when navigating between filters, which addresses some of the navigation issues mentioned in the PR comments.


70-84: Well-implemented controlled/uncontrolled state pattern.

Good implementation of the controlled/uncontrolled state pattern with clear separation of concerns. This makes the component more flexible and reusable.


86-92: Proper cleanup of state when popover closes.

This useEffect hook ensures all state is properly reset when the popover closes, preventing stale state from affecting subsequent interactions.


134-139: Good detection of input focus state.

This properly identifies when an input element is focused, which helps prevent keyboard shortcuts from interfering with typing - directly addressing the hijacking issues mentioned in the PR objectives.


140-153: Improved handling for active filter navigation.

The logic for handling arrow keys when a filter item popover is active has been improved, making it clearer and addressing the navigation issues mentioned in the PR comments.


195-217: Properly updated Drover components with necessary props.

The Drover components are properly updated with the necessary props for the new controlled/uncontrolled pattern. Good addition of the outline-none class for accessibility.


197-207: Elegant solution for dynamic shortcut registration.

The ShortcutActivator components are elegantly integrated, creating a clean way to dynamically register shortcuts for each filter item. This approach helps address the shortcut conflicts mentioned in the PR objectives.


220-231:

Details

❓ Verification inconclusive

Consider verifying keyboard navigation with screen readers.

The keyboard navigation logic looks solid, but it would be worthwhile to verify how this works with screen readers, especially since the PR comments mentioned navigation difficulties.


🏁 Script executed:

#!/bin/bash
# Look for any ARIA-related attributes or roles in related filter components
echo "Checking for ARIA attributes in filter components..."
rg -n -A2 -B2 "aria-" --glob "**/*filter*.tsx"

echo "Checking for role attributes in filter components..."
rg -n -A2 -B2 "role=" --glob "**/*filter*.tsx"

echo "Checking for keyboard handling in related components..."
rg -n -A5 -B5 "handleKeyDown|onKeyDown" --glob "**/*filter*.tsx"

Length of output: 11214


Keyboard navigation ARIA attributes in place – please test with screen readers

The components already include the appropriate role and aria- attributes plus onKeyDown handlers:

• apps/dashboard/components/logs/checkbox/filters-popover.tsx (≈ lines 216–224):
<div role="menu"> container with onKeyDown={handleKeyDown}
• apps/dashboard/components/logs/checkbox/filter-item.tsx (≈ lines 123–131):
role="menuitem", aria-haspopup="true", aria-expanded={open}
• apps/dashboard/components/logs/checkbox/filter-checkbox.tsx (≈ lines 197–201):
aria-checked={…}, onKeyDown={(e) => handleKeyDown…} on labels

No code changes are required—please perform manual testing with a screen reader (e.g., NVDA, VoiceOver) to confirm focus management and announcements meet your accessibility goals.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3434665 and d0f4630.

📒 Files selected for processing (2)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filters-popover.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Test Packages / Test ./packages/rbac
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Packages / Test ./internal/keys
  • GitHub Check: Test Packages / Test ./internal/hash
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
apps/dashboard/components/logs/checkbox/filters-popover.tsx (3)

70-86: Excellent controlled component implementation

The implementation of controlled vs. uncontrolled state is well done. This pattern follows React best practices by supporting both external state management and internal fallback state.


131-194: Improved keyboard navigation logic

The keyboard navigation handling is well-structured with clear separation between:

  1. Child popover navigation (limited to ArrowLeft)
  2. Main filter list navigation (arrow keys)
  3. Filter activation (Enter/ArrowRight)
  4. Popover dismissal (Escape)

This addresses some of the navigation issues mentioned in the PR comments.


196-237: Well-structured rendering with proper accessibility

The render method is well organized with appropriate ARIA roles and focus management. The pattern of rendering null ShortcutActivator components for each item is clever, though unconventional.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (2)
apps/dashboard/components/logs/checkbox/filter-item.tsx (2)

113-113: Add explanation for the biome-ignore comment

The biome-ignore comment is missing the explanation for why the linting rule is being ignored. Always include explanations for linter suppressions to help other developers understand your reasoning.

- {/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
+ {/* biome-ignore lint/a11y/useKeyWithClickEvents: Click events are handled separately, keyboard navigation is managed by parent component */}

52-67: Consider handling focus return when popover closes

While you've implemented focus management for when the popover opens, there's no explicit logic to return focus when it closes. Although the parent component might handle this via isFocused, it's good practice to explicitly handle focus restoration within the component.

You could add another effect to handle focus restoration:

// Focus content when drover becomes active and open
// biome-ignore lint/correctness/useExhaustiveDependencies:  no need to react for label
useEffect(() => {
  if (isActive && open && contentRef.current) {
    // Find and focus the first focusable element within the content
    const focusableElements = contentRef.current.querySelectorAll<HTMLElement>(
      'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])',
    );
    if (focusableElements.length > 0) {
      focusableElements[0].focus({ preventScroll: true });
    } else {
      // Fallback: focus the content container itself if nothing else is focusable
      contentRef.current.focus({ preventScroll: true });
    }
  }
}, [isActive, open, label]); // Depend on isActive and open

+ // Return focus to trigger when popover closes
+ useEffect(() => {
+   // If was previously active and open, but now closed
+   if (!open && !isActive && itemRef.current) {
+     // Return focus to the trigger 
+     itemRef.current.focus({ preventScroll: true });
+   }
+ }, [open, isActive]);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0f4630 and 70105bd.

📒 Files selected for processing (2)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx (1 hunks)
  • apps/dashboard/components/logs/checkbox/filters-popover.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/dashboard/components/logs/checkbox/filters-popover.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Test Packages / Test ./internal/clickhouse
🔇 Additional comments (1)
apps/dashboard/components/logs/checkbox/filter-item.tsx (1)

1-171: Overall implementation looks good with a few concerns

The FilterItem component has good structure with clear separation of concerns between state management, focus handling, and UI rendering. The component handles keyboard navigation for the Escape key and provides good accessibility attributes.

However, based on the PR comments about keyboard navigation issues, it's worth ensuring this implementation addresses those concerns. Specifically:

  1. The tooltip reference to Command key doesn't match the PR's move to option+shift+key
  2. Space/Enter key handling for selection may need improvement
  3. Verify if arrow key navigation within menus works as expected

These issues should be addressed to ensure the keyboard shortcut changes don't negatively impact usability.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/dashboard/components/logs/datetime/datetime-popover.tsx (1)

132-177: Improved mobile UI with simplified toggle and visibility logic

The mobile drawer layout has been significantly improved with:

  1. Better height constraints using max-h-[80vh] instead of fixed height
  2. Cleaner toggle button with improved visual feedback
  3. Simplified visibility toggling using CSS classes instead of animation components

These changes align with the PR's goals of improving UI/UX while fixing keyboard navigation issues.

Consider adding a smooth transition when showing/hiding content to improve the user experience:

-              <div className={cn("w-full", !timeRangeOpen && "hidden")}>
+              <div className={cn("w-full transition-all duration-200 ease-in-out", {
+                "opacity-100 max-h-96 overflow-visible": timeRangeOpen,
+                "opacity-0 max-h-0 overflow-hidden": !timeRangeOpen
+              })}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70105bd and 0f20f4f.

📒 Files selected for processing (7)
  • apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/logs-table.tsx (1 hunks)
  • apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx (2 hunks)
  • apps/dashboard/components/logs/datetime/datetime-popover.tsx (3 hunks)
  • apps/dashboard/components/navigation/navbar.tsx (1 hunks)
  • apps/dashboard/components/ui/drover.tsx (2 hunks)
  • apps/dashboard/components/virtual-table/index.tsx (2 hunks)
  • apps/dashboard/hooks/use-responsive.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/hooks/use-responsive.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/dashboard/components/navigation/navbar.tsx
  • apps/dashboard/app/(app)/apis/[apiId]/_overview/components/table/logs-table.tsx
🧰 Additional context used
🧬 Code Graph Analysis (3)
apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx (1)
apps/dashboard/hooks/use-mobile.tsx (1)
  • useIsMobile (5-19)
apps/dashboard/components/virtual-table/index.tsx (1)
apps/dashboard/hooks/use-mobile.tsx (1)
  • useIsMobile (5-19)
apps/dashboard/components/ui/drover.tsx (1)
apps/dashboard/hooks/use-mobile.tsx (1)
  • useIsMobile (5-19)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Test Packages / Test ./apps/dashboard
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/hash
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/dashboard/components/ui/drover.tsx (1)

3-3: Cleaner responsiveness hook usage

The change from useResponsive to useIsMobile simplifies the code while maintaining the same functionality. This direct hook usage is more focused on the specific need to detect mobile viewports.

Also applies to: 38-38

apps/dashboard/components/virtual-table/index.tsx (1)

1-1: Good migration to focused mobile detection hook

Changing from useResponsive to the focused useIsMobile hook maintains the same mobile detection functionality with a simpler implementation. The hook's return value continues to be used consistently throughout the component.

Also applies to: 64-64

apps/dashboard/app/(app)/apis/[apiId]/api-id-navbar.tsx (1)

8-8: Consistent implementation of mobile detection

The switch to useIsMobile maintains the same functionality while aligning with the codebase-wide refactoring pattern. The boolean value continues to be used appropriately for conditional rendering.

Also applies to: 32-32

apps/dashboard/components/logs/datetime/datetime-popover.tsx (1)

7-7: Consistent implementation of mobile detection

Switching to useIsMobile aligns with the pattern established in other components while maintaining the same functionality for responsive UI detection.

Also applies to: 39-39

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/dashboard/components/logs/checkbox/filter-item.tsx (2)

135-135: Update keyboard shortcut tooltip to match actual implementation

The tooltip references ${shortcut?.toUpperCase()} without mentioning the modifier keys. According to the PR objectives, shortcuts are being changed to use option+shift+key combinations. This creates inconsistency between the displayed tooltip and the actual keyboard shortcut implementation.

- title={`Press '${shortcut?.toUpperCase()}' to toggle ${label} options`}
+ title={`Press 'option+shift+${shortcut?.toUpperCase()}' to toggle ${label} options`}

114-128: Improve keyboard accessibility by handling key events on the trigger

The trigger div handles click events but lacks keyboard event handlers for Enter and Space keys. This prevents users from activating the filter item using keyboard navigation.

<div
  ref={itemRef}
  className={cn(
    "flex w-full items-center px-2 py-1.5 justify-between rounded-lg group cursor-pointer",
    "hover:bg-gray-3 data-[state=open]:bg-gray-3",
    "focus:outline-none focus:ring-2 focus:ring-accent-7",
    isFocused && !isActive ? "bg-gray-4" : "",
    isActive ? "bg-gray-3" : "",
  )}
  tabIndex={-1}
  role="menuitem"
  aria-haspopup="true"
  aria-expanded={open}
  onClick={handleTriggerClick}
+ onKeyDown={(e) => {
+   if (e.key === "Enter" || e.key === " ") {
+     e.preventDefault();
+     handleTriggerClick();
+   }
+ }}
>
🧹 Nitpick comments (3)
apps/dashboard/components/logs/checkbox/filter-item.tsx (3)

69-84: Extend keyboard support for nested menu navigation

Currently, the nested Drover only handles the Escape key for closing the menu, but doesn't implement handlers for arrow keys or Enter/Space for selection in the nested content. Users reported difficulties with keyboard navigation in the PR comments.

Consider implementing a more comprehensive keyboard navigation system that:

  1. Handles ArrowUp/Down for navigating nested menu items
  2. Supports Enter/Space for activating selected items
  3. Maintains focus when navigating through nested submenus

This can be addressed in a future iteration as mentioned in your response to a previous similar comment.


45-50: Consider simplifying focus management with a combined useEffect

The component has multiple useEffect hooks managing focus in different conditions, which increases complexity and could lead to unexpected behavior.

Consider combining related focus management logic into a single useEffect that handles all focus scenarios, making the code more maintainable and reducing the risk of conflicting focus management.

- // biome-ignore lint/correctness/useExhaustiveDependencies:  no need to react for label
- useEffect(() => {
-   if (isFocused && !isActive && itemRef.current) {
-     // Only focus trigger if not active
-     itemRef.current.focus({ preventScroll: true });
-   }
- }, [isFocused, isActive, label]); // Depend on isActive too
- 
- // Focus content when drover becomes active and open
- // biome-ignore lint/correctness/useExhaustiveDependencies:  no need to react for label
- useEffect(() => {
-   if (isActive && open && contentRef.current) {
-     // Find and focus the first focusable element within the content
-     const focusableElements = contentRef.current.querySelectorAll<HTMLElement>(
-       'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])',
-     );
-     if (focusableElements.length > 0) {
-       focusableElements[0].focus({ preventScroll: true });
-     } else {
-       // Fallback: focus the content container itself if nothing else is focusable
-       contentRef.current.focus({ preventScroll: true });
-     }
-   }
- }, [isActive, open, label]); // Depend on isActive and open

+ // biome-ignore lint/correctness/useExhaustiveDependencies: no need to react for label
+ useEffect(() => {
+   if (isFocused && !isActive && itemRef.current) {
+     // Only focus trigger if not active
+     itemRef.current.focus({ preventScroll: true });
+   } else if (isActive && open && contentRef.current) {
+     // Find and focus the first focusable element within the content
+     const focusableElements = contentRef.current.querySelectorAll<HTMLElement>(
+       'button, [href], input:not([type="hidden"]), select, textarea, [tabindex]:not([tabindex="-1"])',
+     );
+     if (focusableElements.length > 0) {
+       focusableElements[0].focus({ preventScroll: true });
+     } else {
+       // Fallback: focus the content container itself if nothing else is focusable
+       contentRef.current.focus({ preventScroll: true });
+     }
+   }
+ }, [isFocused, isActive, open, label]); // Depend on all relevant state

160-165: Consider adding explicit keyboard focus styling

The Drover.Content element has tabIndex={-1} which means it can receive programmatic focus, but there's no explicit focus styling in the className.

<Drover.Content
  ref={contentRef}
- className="min-w-60 w-full bg-gray-1 dark:bg-black drop-shadow-2xl p-0 border-gray-6 rounded-lg"
+ className="min-w-60 w-full bg-gray-1 dark:bg-black drop-shadow-2xl p-0 border-gray-6 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent-7"
  side="right"
  align="start"
  sideOffset={12}
  onKeyDown={handleItemDroverKeyDown}
  tabIndex={-1}
>

Adding focus styling ensures users can see when the content has focus, improving the keyboard navigation experience.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f20f4f and 1b63e07.

📒 Files selected for processing (1)
  • apps/dashboard/components/logs/checkbox/filter-item.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/dashboard/components/logs/checkbox/filter-item.tsx (5)
apps/dashboard/components/logs/checkbox/filters-popover.tsx (1)
  • FilterItemConfig (17-23)
apps/dashboard/components/ui/drover.tsx (1)
  • Drover (189-198)
apps/dashboard/components/keyboard-button.tsx (1)
  • KeyboardButton (11-38)
internal/ui/src/components/button.tsx (1)
  • Button (443-443)
internal/icons/src/icons/caret-right.tsx (1)
  • CaretRight (15-37)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Test Packages / Test ./packages/rbac
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/id
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (2)
apps/dashboard/components/logs/checkbox/filter-item.tsx (2)

1-8: LGTM! Well-structured imports

The import statements are well-organized, importing components and utilities from appropriate locations.


9-21: LGTM! Well-defined type interfaces

The type definitions for FilterItemConfig and FilterItemProps are clear and well-structured, providing good type safety for the component.

@chronark chronark added this pull request to the merge queue Apr 25, 2025
Merged via the queue into main with commit eeacd6c Apr 25, 2025
30 checks passed
@chronark chronark deleted the fix-keyboard-hijack branch April 25, 2025 15:07
@coderabbitai coderabbitai bot mentioned this pull request Dec 12, 2025
19 tasks
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.

2 participants