Skip to content

Add keyboard hotkey support to menu items#3818

Open
Kiarokh wants to merge 6 commits intomainfrom
menu-hotkeys
Open

Add keyboard hotkey support to menu items#3818
Kiarokh wants to merge 6 commits intomainfrom
menu-hotkeys

Conversation

@Kiarokh
Copy link
Contributor

@Kiarokh Kiarokh commented Feb 2, 2026

Summary by CodeRabbit

  • New Features

    • Menu items can declare hotkeys for direct selection while open; hotkey support added across menus and meta items.
    • New hotkey component and utilities normalize and emit hotkey triggers; platform-aware device detection added.
  • UI

    • Menu trigger updated and hotkey-enabled meta entries rendered in menus.
  • Tests

    • Added E2E and unit tests for hotkey selection, reserved-key behavior, normalization, and focus handling.
  • Documentation

    • Examples and docs expanded with hotkey usage, reserved keys, and platform guidance.
  • Style

    • Updated keyboard token styling for improved visuals.

Review:

  • Commits are atomic
  • Commits have the correct type for the changes made
  • Commits with breaking changes are marked as such

Browsers tested:

(Check any that applies, it's ok to leave boxes unchecked if testing something didn't seem relevant.)

Windows:

  • Chrome
  • Edge
  • Firefox

Linux:

  • Chrome
  • Firefox

macOS:

  • Chrome
  • Firefox
  • Safari

Mobile:

  • Chrome on Android
  • iOS

@github-actions
Copy link

github-actions bot commented Feb 2, 2026

Documentation has been published to https://lundalogik.github.io/lime-elements/versions/PR-3818/

@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

📝 Walkthrough

Walkthrough

Adds first-class hotkey support: new hotkey MenuItem property, hotkey normalization utilities, a visual limel-hotkey component emitting hotkeyTrigger, menu-surface key capture and matching while open, renderer/meta updates, examples, E2E tests, and device helper adjustments.

Changes

Cohort / File(s) Summary
Hotkey utilities & tests
src/util/hotkeys.ts, src/util/hotkeys.spec.ts
New hotkey library: tokenization, normalization, KeyboardEvent→canonical hotkey mapping, text-input detection, and unit tests for letters, digits, function/arrow keys, modifiers, and plus-sign handling.
Hotkey UI & types
src/components/hotkey/hotkey.tsx, src/components/hotkey/hotkey.types.ts, src/components/hotkey/hotkey.scss, src/components/markdown/partial-styles/_kbd.scss
New limel-hotkey component with platform-aware glyph/text rendering, emits hotkeyTrigger (typed), and updated kbd styling.
Hotkey examples
src/components/hotkey/examples/hotkey-basic.tsx, src/components/hotkey/examples/hotkey-basic.scss, src/components/hotkey/examples/hotkey-disabled.tsx, src/components/hotkey/examples/hotkey-duplicates.tsx, src/components/menu/examples/menu-hotkeys.tsx
Added examples demonstrating basic, disabled, duplicate hotkeys and an example menu wired with hotkey-enabled items and Host-wrapped render/trigger.
Menu runtime & tests
src/components/menu/menu.tsx, src/components/menu/menu.e2e.ts, src/components/menu/menu.types.ts
Added hotkey?: string to MenuItem, wired menu-surface key handling, normalized-hotkey caching/matching, reserved-key filtering, select-on-hotkey (only when open), and E2E helpers/tests for hotkey and reserved-key behavior.
Meta rendering & list integration
src/components/list-item/menu-item-meta/menu-item-meta.tsx, src/components/menu-list/menu-list-renderer.tsx
MenuItemMeta now accepts hotkey and disabled, prefers rendering <limel-hotkey> over commandText; renderer propagates hotkey into meta props and detects meta presence accordingly.
Device helpers
src/util/device.ts
Refactored UA/platform/touch helpers and added isAppleDevice() used for platform-specific glyph rendering and behavior.
Misc. styles & scaffolding
src/components/hotkey/..., src/components/markdown/..., src/components/list-item/...
Global <kbd> style tweaks, minor style and example adjustments to integrate hotkey display into menu meta rendering and examples.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(226, 240, 255, 0.5)
    actor User
    end
    participant Surface as MenuSurface
    participant Menu as LimelMenu
    participant Utils as HotkeyUtils
    participant Items as MenuItems
    participant Evt as SelectEvent

    User->>Surface: press keyboard (e.g. Alt+C)
    Surface->>Menu: forward keydown to surface handler
    Menu->>Utils: isKeyboardEventFromTextInput(event)?
    Utils-->>Menu: false
    Menu->>Utils: hotkeyFromKeyboardEvent(event)
    Utils-->>Menu: "alt+c"
    Menu->>Menu: isReservedMenuHotkey? (no)
    Menu->>Items: find visible item with hotkey "alt+c"
    Items-->>Menu: matching item found
    Menu->>Surface: event.preventDefault()
    Menu->>Evt: emit select for matched item
    Evt-->>User: item selected / menu closed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

accessibility, maintenance

Suggested reviewers

  • devbymadde
  • omaralweli
  • LucyChyzhova
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch menu-hotkeys

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 and usage tips.

@Kiarokh Kiarokh force-pushed the menu-hotkeys branch 2 times, most recently from e2c148e to 530a80c Compare February 2, 2026 16:00
@Kiarokh Kiarokh marked this pull request as ready for review February 2, 2026 16:00
@Kiarokh Kiarokh requested a review from a team as a code owner February 2, 2026 16:00
@Kiarokh Kiarokh self-assigned this Feb 2, 2026
@Kiarokh Kiarokh added feature New feature or request usability Things that make it harder or easier for users to understand or use elements labels Feb 2, 2026
@coderabbitai coderabbitai bot changed the title @coderabbitai Add keyboard hotkey support to menu items Feb 2, 2026
Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/menu/examples/menu-hotkeys.tsx (1)

73-88: 🛠️ Refactor suggestion | 🟠 Major

Wrap multiple JSX elements in <Host> instead of an array literal.

Per the coding guidelines for StencilJS .tsx files, when returning multiple top-level JSX elements from the render method, they should be wrapped in the special <Host> element rather than an array literal.

♻️ Proposed fix
+import { Host } from '@stencil/core';
     public render() {
-        return [
+        return (
+            <Host>
+                <limel-menu items={this.items} onSelect={this.handleSelect}>
+                    <limel-icon-button
+                        label="Menu"
+                        icon="menu_2"
+                        elevated={true}
+                        slot="trigger"
+                    />
+                </limel-menu>
+                <limel-example-value
+                    label="Last selected item"
+                    value={this.lastSelectedItem}
+                />
+            </Host>
+        );
-            <limel-menu items={this.items} onSelect={this.handleSelect}>
-                <limel-icon-button
-                    label="Menu"
-                    icon="menu_2"
-                    elevated={true}
-                    slot="trigger"
-                />
-            </limel-menu>,
-            <limel-example-value
-                label="Last selected item"
-                value={this.lastSelectedItem}
-            />,
-        ];
     }

As per coding guidelines: "When returning multiple JSX elements from the render method, never wrap them in an array literal. Instead, always wrap them in the special <Host> element."

🤖 Fix all issues with AI agents
In `@src/components/menu/menu.tsx`:
- Around line 367-385: In findMenuItemByHotkey, remove the redundant type cast
by relying on the isMenuItem type guard: replace the line that assigns rawHotkey
= (item as MenuItem).hotkey with a direct access const rawHotkey = item.hotkey;
(keep the existing checks using this.isMenuItem, this.getNormalizedHotkey,
item.disabled and this.visibleItems unchanged).

In `@src/util/hotkeys.ts`:
- Around line 47-55: The return logic in the normalization branch is redundant:
after checking if /^f\d{1,2}$/.test(normalized) and returning normalized, the
subsequent if (normalized.length === 1) branch also returns normalized and the
final return returns normalized again; simplify by removing the length check and
directly returning normalized if the regex test fails. Update the hotkey
normalization function to keep the /^f\d{1,2}$/ early return and replace the
remaining branches with a single return normalized to preserve existing behavior
(refer to the normalized variable and the regex check).

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/menu/menu.e2e.ts`:
- Around line 265-306: Tests use fixed waits (page.waitForTimeout(200/50)),
causing flakiness; replace those with deterministic checks: after
limelMenu.setProperty('items') and trigger.click(), wait for the menu to be open
or the focused item to appear (for example, await page.waitForFunction to assert
trigger.getAttribute('aria-expanded') === 'true' or wait for a focused menu item
via page.find('li[aria-selected="true"], li:focus') ) before pressing keys with
page.keyboard.press('Enter'/'ArrowDown'), and remove the waitForTimeout calls so
selection assertions (spy on 'select') run only after the menu/focus state is
confirmed.

In `@src/util/hotkeys.ts`:
- Around line 203-231: The isKeyboardEventFromTextInput function currently
treats missing composedPath() as an empty path and can miss text inputs; update
isKeyboardEventFromTextInput to fall back to a single-node path using
event.target (and/or event.srcElement for legacy IE) when composedPath is not
available, then run the same checks (isContentEditable, tagName
INPUT/TEXTAREA/SELECT, role="textbox") against that fallback node so hotkeys
won't fire while typing in unsupported browsers.

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/hotkey/hotkey.tsx`:
- Around line 66-73: normalizeHotkeyString currently splits on "+" and drops
empty tokens so a lone this.value of "+" becomes null and can never match
hotkeyFromKeyboardEvent; fix by special-casing the exact string "+" inside
normalizeHotkeyString (or the caller before normalizing) to return the canonical
token that normalizeKey expects (e.g., map "+" to the supported token name) so
pressed comparisons work, or alternatively update the docs/examples to use the
supported token instead of "+"; ensure the mapping uses the same normalized form
returned by normalizeKey so the pressed === expected check succeeds.

@Kiarokh Kiarokh force-pushed the menu-hotkeys branch 3 times, most recently from dd96fcb to 1a3d2c4 Compare February 3, 2026 14:07
@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

@Kiarokh, I apologize for the confusion. Let me trigger the complete fresh review now. I'll review all the current changes in the PR with a clean perspective on the new code structure and strategy.

✅ Actions performed

Full review triggered.

@Lundalogik Lundalogik deleted a comment from coderabbitai bot Feb 3, 2026
Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/hotkey/hotkey.tsx`:
- Around line 55-61: The mapped <kbd> elements rendered in parts.map(...) lack a
key which breaks the useJsxKeyInIterable lint rule; update the map callback in
the render (the parts.map producing <kbd> and calling this.formatDisplayToken)
to provide a unique key for each <kbd> — e.g., use the part value or an index
fallback (key={part} or key={`${part}-${i}`}) so each <kbd> inside Host has a
stable key.

In `@src/util/device.ts`:
- Around line 49-60: The return expression in isAppleDevice() is redundant
because isIPadOS13Plus is defined as (isMacLike && getMaxTouchPoints() > 1), so
including it separately is unnecessary; update the function to simplify the
final return to "isIPadIPhoneIPod || isMacLike" (or remove isIPadOS13Plus usage
while keeping the variable for documentation if preferred) and ensure variables
getUserAgent(), getPlatform(), isIPadIPhoneIPod, isMacLike, and isIPadOS13Plus
are left intact for clarity.

In `@src/util/hotkeys.spec.ts`:
- Around line 14-40: Add unit tests for hotkeyFromKeyboardEvent to cover
modifier combinations, function keys, arrow keys, and code-based normalization:
add a test like "includes multiple modifiers" that asserts
hotkeyFromKeyboardEvent returns "ctrl+alt+a" for an event with key 'a' and
ctrlKey/altKey true; add "handles function keys" asserting 'f1' and 'f12' for
events with key 'F1'/'F12'; add "handles arrow keys" asserting
'arrowleft'/'arrowright' for ArrowLeft/ArrowRight keys; and add "uses event.code
for non-QWERTY" that simulates a non-letter key where key differs from code
(e.g., key='@' code='Quote' or code-based letter) to ensure the function
normalizes using event.code when appropriate by comparing expected normalized
output from hotkeyFromKeyboardEvent.

Comment on lines +49 to +60
export function isAppleDevice(): boolean {
const ua = getUserAgent();
const platform = getPlatform();

const isIPadIPhoneIPod = /iPad|iPhone|iPod/i.test(ua);
const isMacLike = /Mac/i.test(platform);

// iPadOS can report itself as Mac; use touch points to detect it.
const isIPadOS13Plus = isMacLike && getMaxTouchPoints() > 1;

return isIPadIPhoneIPod || isIPadOS13Plus || isMacLike;
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider simplifying the return expression.

The logic is correct, but isIPadOS13Plus is redundant in the return statement since it implies isMacLike by definition (isIPadOS13Plus = isMacLike && ...). The expression isIPadIPhoneIPod || isIPadOS13Plus || isMacLike is equivalent to isIPadIPhoneIPod || isMacLike.

That said, keeping isIPadOS13Plus explicit could be intentional for documentation purposes—it makes the three detection cases clearer to readers.

♻️ Optional simplification
-    return isIPadIPhoneIPod || isIPadOS13Plus || isMacLike;
+    // iPadOS13Plus is a subset of isMacLike, so we can simplify:
+    return isIPadIPhoneIPod || isMacLike;

Or keep as-is if the explicit enumeration of cases is preferred for readability.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function isAppleDevice(): boolean {
const ua = getUserAgent();
const platform = getPlatform();
const isIPadIPhoneIPod = /iPad|iPhone|iPod/i.test(ua);
const isMacLike = /Mac/i.test(platform);
// iPadOS can report itself as Mac; use touch points to detect it.
const isIPadOS13Plus = isMacLike && getMaxTouchPoints() > 1;
return isIPadIPhoneIPod || isIPadOS13Plus || isMacLike;
}
export function isAppleDevice(): boolean {
const ua = getUserAgent();
const platform = getPlatform();
const isIPadIPhoneIPod = /iPad|iPhone|iPod/i.test(ua);
const isMacLike = /Mac/i.test(platform);
// iPadOS can report itself as Mac; use touch points to detect it.
const isIPadOS13Plus = isMacLike && getMaxTouchPoints() > 1;
// iPadOS13Plus is a subset of isMacLike, so we can simplify:
return isIPadIPhoneIPod || isMacLike;
}
🤖 Prompt for AI Agents
In `@src/util/device.ts` around lines 49 - 60, The return expression in
isAppleDevice() is redundant because isIPadOS13Plus is defined as (isMacLike &&
getMaxTouchPoints() > 1), so including it separately is unnecessary; update the
function to simplify the final return to "isIPadIPhoneIPod || isMacLike" (or
remove isIPadOS13Plus usage while keeping the variable for documentation if
preferred) and ensure variables getUserAgent(), getPlatform(), isIPadIPhoneIPod,
isMacLike, and isIPadOS13Plus are left intact for clarity.

@Kiarokh Kiarokh force-pushed the menu-hotkeys branch 2 times, most recently from a69816e to bf75cb7 Compare February 4, 2026 10:19
Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/hotkey/hotkey.tsx`:
- Around line 55-61: The kbd elements rendered in the Host mapping currently set
aria-hidden="true" which triggers the a11y lint rule; remove the aria-hidden
attribute from the <kbd> elements in the parts.map render soScreen readers can
access the keys while keeping the Host's aria-label (refer to Host, parts.map
and formatDisplayToken in hotkey.tsx to locate the code). Ensure no other
aria-hidden is added and that formatDisplayToken continues to produce the
visible label for each kbd.

In `@src/components/menu/menu.e2e.ts`:
- Around line 362-381: The test dispatches the KeyboardEvent to
limel-menu-surface immediately after clicking the trigger, which can race if the
surface hasn't opened; after clicking the trigger (the variable trigger and the
call to trigger.click()/page.waitForChanges()), wait deterministically for the
surface to exist and be open/visible (e.g., use page.waitForSelector or a short
polling loop to confirm document.querySelector('limel-menu-surface') is present
and has the open/visible state) before executing the evaluated block that
dispatches the KeyboardEvent; update the code that finds limel-menu-surface and
dispatches the event (the evaluate callback) to run only once that deterministic
wait completes.

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/hotkey/hotkey.tsx`:
- Around line 78-83: The UI shows 'meta' as 'Ctrl' for non-Apple (case 'meta' /
'cmd' / 'command' returning isApple ? '⌘' : 'Ctrl') but matching still expects
Meta, so fix by normalizing the incoming hotkey tokens when not on Apple: in the
hotkey normalization function (the code that processes 'lower' tokens before
matching, e.g., where you switch on lower) map 'meta'/'cmd'/'command' → 'ctrl'
for non-Apple platforms so both display and matching align (alternatively, if
you prefer to keep matching as Meta, change the display branch to render
'Meta'/'Win' instead—choose one consistent approach and apply it in the switch
handling and the hotkey normalization logic).

);
}

private formatDisplayToken(token: string, isApple: boolean): string {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to live inside the component? We can move it to a separate function for easier testing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it could, but I also wanted to use it to add a class to render the glyphs a bit larger than the letters. added here: 195e489

Is it a big deal?

Copy link
Contributor

Choose a reason for hiding this comment

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

No it's not 😄 Just separation of concerns

Comment on lines +120 to +122
if ((event as any)[Hotkey.handledFlag]) {
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need this check? Do we expect the event to propagate though multiple hotkeys? It does not look like they will ever be nested inside each other

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added it because of some discussions I had with a 🤖 friend, when trying to mitigate buggy behaviours, due to having multiple instances of the same component potentially rendered all at once.

Asked it to explain, and it said:

That check is there to prevent duplicate handling of the same physical keypress when there are multiple limel-hotkey instances on the page, not because they’re nested.

What happens otherwise:

Every limel-hotkey registers @Listen('keydown', { target: 'document', capture: true }).
A single keydown will invoke handleKeyDown() once per instance.
If you render several hotkeys (like in the example, or a menu with many items), each instance will evaluate the event; without a guard, more than one instance can emit if they happen to match (or if you accidentally have duplicates).
Why a flag on the event:

stopPropagation() isn’t ideal here because we’re listening on document in capture and we don’t want to interfere with other global handlers in the app.
The per-event flag makes it “handled once” across all limel-hotkey listeners, regardless of render order.
So yes: we do expect the same event to “reach” multiple hotkey components because they all listen globally, even though they aren’t nested.

If you’re confident you’ll never have duplicate hotkeys active at the same time, you could remove it—but in menus/examples it’s very easy to have multiple limel-hotkey elements present, so it’s a safety net.

Copy link
Contributor

Choose a reason for hiding this comment

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

But with this check, it will be unclear which of those hotkeys will be triggered. There is no way for the consumer to know if their instance will be allowed or blocked by this check. Is that the behavior we want? The way it will work now will most likely be the component that renders first is the one that will receive the event

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm… u r right.
what's a better approach? Just remove it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I think we should remove it. What does the 🤖 say?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I've kept the mechanics to trigger a console warning when a duplicate hotkey is identified. ec3a5c0
This way, the limel-hotkey component will only work for the first instance, and reduces a little bit of the confusion risk for the end-user. And the consumer can hopefully see a warning in the console.

Most of this responsibility is on the consumer anyway. But it's good to have some error prevention in the component itself as well.

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/hotkey/examples/hotkey-basic.tsx`:
- Around line 15-24: Typo: the docs line "`cmd` or `command` are is just an
alias for `meta`" contains a duplicated word; update the sentence in the hotkey
example text so it reads correctly (e.g., "`cmd` or `command` is just an alias
for `meta`" or "`cmd` and `command` are just aliases for `meta`")—look for the
string containing `cmd`, `command`, and `meta` in hotkey-basic.tsx and fix the
duplicated "are is" to the chosen correct phrasing.

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/menu/menu.e2e.ts`:
- Around line 203-285: Duplicate logic in waitForFocusedMenuItemIndex: extract
the core DOM-inspection code from getIndex into a single serializable function
(e.g., const findFocusedIndex = function() { ... } or a template literal string)
and reuse that in both the page.evaluate(getIndex) call and the
anyPage.waitForFunction(...) call; update waitForFocusedMenuItemIndex to
reference the shared findFocusedIndex (or its .toString()/string form) when
calling page.evaluate and when passing the predicate to anyPage.waitForFunction
so there is only one source of truth for locating the active
'.mdc-deprecated-list-item'.

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/list-item/menu-item-meta/menu-item-meta.tsx (1)

25-63: ⚠️ Potential issue | 🟠 Major

Add documentation/example for the new hotkey prop.
A new prop was added to an existing component, but no usage example or explanation is shown. Please add/update the relevant component documentation (e.g., menu item docs) to demonstrate hotkey usage and behavior.

As per coding guidelines, src/components/**/*.tsx: “When contributors add new props to existing components in the lime-elements repository, they should always add documentation examples that demonstrate the new prop's usage and explain how it works.”

Copy link

@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

🤖 Fix all issues with AI agents
In `@src/components/hotkey/examples/hotkey-basic.tsx`:
- Line 1: The example currently types the hotkey event payload inline as "{
hotkey: string }"; import the publicly exported type LimelHotkeyTriggerDetail
from '@limetech/lime-elements' and replace the inline type with
LimelHotkeyTriggerDetail in the event handler(s) (every place that currently
uses "{ hotkey: string }", including the handler around the hotkey trigger at
lines ~63-65). Update the top import statement to include
LimelHotkeyTriggerDetail and adjust the handler signature and any usages to
reference .value/.hotkey/.keyboardEvent from LimelHotkeyTriggerDetail for full
type safety.

In `@src/components/menu/menu.e2e.ts`:
- Around line 408-454: The test dispatches a KeyboardEvent and immediately
asserts on the spy and menu open state which can be asynchronous; after the
page.evaluate dispatch and the existing await page.waitForChanges(), add
deterministic waits for the menu to finish closing and the trigger aria state
before asserting — e.g. call waitForMenuOpenState(page, false),
waitForMenuSurfaceOpenState(page, false) and waitForTriggerAriaExpanded(page,
'button[slot="trigger"]', false) (or an equivalent waitForFunction that ensures
the spy has been called) then run the expect(spy)... and expect(isOpen)...
assertions so the test is stable.
- Around line 84-149: The wait helper's page.evaluate callbacks currently return
false when the 'limel-menu-surface' element is missing, causing a hang when
waiting for open=false; update both evaluator functions (the arrow callbacks
passed to page.evaluate that accept the isOpen parameter and query
'limel-menu-surface' into the local variable surface) so that if surface is
null/undefined you return true when isOpen is false (i.e. treat a missing
surface as closed), but keep the existing logic for the isOpen=true case; ensure
you change both the immediate wait block and the one inside the while loop so
they behave identically.

@Kiarokh Kiarokh force-pushed the menu-hotkeys branch 3 times, most recently from 09d0d79 to f1ae8a6 Compare February 5, 2026 10:33
- Made it safe to import outside the browser
(removed top-level `window.navigator.userAgent` access)
- Added export function `isAppleDevice(): boolean`
for rendering hotkeys, and emitting events when
hotkeys are pressed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request usability Things that make it harder or easier for users to understand or use elements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants