Skip to content

feat(evo-react): add menu - #787

Open
HenriqueLimas wants to merge 10 commits into
mainfrom
evo-menu-react
Open

feat(evo-react): add menu#787
HenriqueLimas wants to merge 10 commits into
mainfrom
evo-menu-react

Conversation

@HenriqueLimas

@HenriqueLimas HenriqueLimas commented Aug 3, 2026

Copy link
Copy Markdown
Member

Description

Adds EvoMenu to @evo-web/react as an extensible compound component with command, radio, and checkbox items sharing one menu-wide roving tabindex and typeahead controller.

API approach

We considered three approaches:

1. EvoMenuItem single component

Based on the selected property being passed as array or one item, it does define the type of the component

// Checkbox
<EvoMenu selected={[optionA, optionB]}>
  <EvoMenuItem value="optionA" />   
  <EvoMenuItem value="optionB" />   
  <EvoMenuItem value="optionC" />
</EvoMenu>

// Radio
<EvoMenu selected={optionA}>
  <EvoMenuItem value="optionA" />   
  <EvoMenuItem value="optionB" />   
  <EvoMenuItem value="optionC" />
</EvoMenu>

// Radio
<EvoMenu>
  <EvoMenuItem />   
  <EvoMenuItem />   
  <EvoMenuItem />
</EvoMenu>

Pros:

  • One single component re-usable
  • Less verbose

Cons:

  • Type definition can't make value required for radio/checkbox (it is necessary when removing React.Children api usage)
  • Doesn't allow to have radio and checkbox on the same menu at the same time

2. EvoMenuItem, EvoMenuRadioItem, and EvoMenuCheckboxItem components

Expose explicit EvoMenuItem, EvoMenuRadioItem, and EvoMenuCheckboxItem components, with provider-only radio and checkbox selection groups.

<EvoMenu>
  <EvoMenuRadioGroup selected="">
    <EvoMenuRadioItem value="" />
    <EvoMenuRadioItem value="" />
  </EvoMenuRadioGroup>

  <EvoMenuCheckboxGroup selected={["a", "b"]}>
     <EvoMenuCheckboxItem value="" />
  </EvoMenuCheckboxGroup>
</EvoMenu>

Pros:

  • Type definition is specific for each component type
  • Allow using multiple options on the same menu (Future proof)

Cons:

  • Verbose

This PR uses the second approach. Separate item components make each semantic type explicit, require value only for selectable items, expose the correct ARIA role without runtime child inspection, and avoid a growing matrix of conditional props and rendering branches. They also allow command, radio, and checkbox items to participate in one menu while their groups own only controlled/uncontrolled selection state. Shared focus, activation, disabled handling, and keyboard behavior remain centralized internally.

Additional changes:

  • Added useRovingTabIndex and useTypehead hooks
  • Adds composed badges, separators, footer content for better composability
  • Uses a11yText and a11yLabelId consistently in React and Marko for the menu's accessible name.
  • Updates Skin so a composed EvoMenuItemBadge receives existing badge positioning through :has(.badge).

Screenshots

Storybook link

Checklist

  • I verify the linked issue has been triaged ("Needs Triage" label removed)
  • I verify all changes are within scope of the linked issue
  • I added/updated/removed testing (Storybook in Skin) coverage as appropriate
  • I tested the UI in all supported browsers
  • I tested the UI in dark mode and RTL mode

Copilot AI review requested due to automatic review settings August 3, 2026 23:04
@changeset-bot

changeset-bot Bot commented Aug 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6b47f72

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@ebay/skin Patch
@evo-web/marko Patch
@evo-web/react Patch

Not sure what this means? Click here to learn what changesets are.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new EvoMenu compound component to @evo-web/react (with command, radio, and checkbox items sharing a single roving-tabindex + typeahead system), aligns accessible naming for evo-menu in Marko to use a11yText/a11yLabelId, and updates Skin menu badge positioning to work without a dedicated “badged” modifier class.

Changes:

  • Add @evo-web/react/menu compound component implementation (context, items, groups, item variants, badge/separator/footer) plus shared useRovingTabIndex + useTypeahead utilities.
  • Update Skin menu badge positioning to support composed badges via :has(.badge) and regenerate dist.
  • Update Marko evo-menu accessible-name API and snapshots; add React + Marko tests/stories and migration guidance.

Reviewed changes

Copilot reviewed 28 out of 29 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/skin/src/sass/menu/stories/misc.stories.js Updates menu “badged” examples to no longer rely on menu__item--badged.
packages/skin/src/sass/menu/menu.scss Adds :has(.badge) styling to position menu badges without needing --badged.
packages/skin/dist/menu/menu.css Regenerated dist CSS reflecting the :has(.badge) selector changes.
packages/evo-react/src/utils/use-typeahead.ts Adds shared typeahead hook used by menu keyboard navigation.
packages/evo-react/src/utils/use-roving-tab-index.ts Adds shared roving tabindex hook used by menu items.
packages/evo-react/src/menu/types.ts Defines public React menu types (props, events, group APIs).
packages/evo-react/src/menu/menu.tsx Adds EvoMenu root + provider wiring and Skin import.
packages/evo-react/src/menu/context.tsx Adds React contexts for menu + items + selection groups.
packages/evo-react/src/menu/menu-items.tsx Implements the role="menu" container with typeahead + roving handlers.
packages/evo-react/src/menu/menu-item.tsx Implements command/radio/checkbox items with shared activation + focus behavior.
packages/evo-react/src/menu/menu-radio-group.tsx Adds controlled/uncontrolled radio selection provider.
packages/evo-react/src/menu/menu-checkbox-group.tsx Adds controlled/uncontrolled checkbox selection provider.
packages/evo-react/src/menu/menu-item-badge.tsx Adds composed badge wrapper for menu usage.
packages/evo-react/src/menu/menu-separator.tsx Adds separator component.
packages/evo-react/src/menu/menu-footer.tsx Adds footer component.
packages/evo-react/src/menu/index.ts Exports menu public API.
packages/evo-react/src/menu/menu.stories.tsx Adds Storybook docs/examples for the compound API.
packages/evo-react/src/menu/README.md Links to Storybook docs for EvoMenu.
packages/evo-react/src/menu/test/test.server.tsx Adds SSR snapshot coverage for menu variants/structure.
packages/evo-react/src/menu/test/test.browser.tsx Adds browser interaction coverage (roving, typeahead, selection, disabled).
packages/evo-react/src/menu/test/snapshots/test.server.tsx.snap Adds SSR snapshots for EvoMenu.
packages/evo-marko/src/tags/evo-menu/index.marko Updates Marko menu accessible-name API to a11yText/a11yLabelId.
packages/evo-marko/src/tags/evo-menu/menu.stories.ts Adds Storybook controls for new a11y inputs.
packages/evo-marko/src/tags/evo-menu/test/test.server.ts Updates Marko SSR tests to use a11yText/a11yLabelId.
packages/evo-marko/src/tags/evo-menu/test/snapshots/test.server.ts.snap Updates Marko snapshots for renamed a11y inputs.
packages/evo-marko/src/tags/evo-menu-button/index.marko Adjusts menu-button typing to continue supporting aria-label/labelledby externally.
.claude/skills/evo-app-migrate-react/SKILL.md Adds ebay-menuevo-menu migration doc entry.
.claude/skills/evo-app-migrate-react/components/evo-menu.md Adds detailed migration guidance for menu usage + new APIs.
.changeset/add-evo-menu.md Changeset for adding EvoMenu + Skin/a11y API alignment.

Comment thread packages/evo-react/src/menu/types.ts Outdated
Comment thread packages/evo-react/src/utils/use-roving-tab-index.ts
Comment on lines +31 to +38
className={classNames(
classPrefix ? `${baseClass}__menu` : "menu",
variant === "filter" && `${baseClass}--filter`,
classPrefix && reverse && `${baseClass}__menu--reverse`,
classPrefix && fixed && `${baseClass}__menu--fixed`,
classPrefix && fixWidth && `${baseClass}__menu--fix-width`,
className,
)}
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

PR Preview Deployed

Websiteevo-markoevo-reactebayui-coreebayui-core-reactskin

commit 6b47f72

# Conflicts:
#	.claude/skills/evo-app-migrate-react/SKILL.md
HenriqueLimas and others added 8 commits August 10, 2026 16:54
# Conflicts:
#	.claude/skills/evo-app-migrate-react/SKILL.md
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
# Conflicts:
#	packages/evo-react/src/utils/use-roving-tab-index.ts
# Conflicts:
#	.claude/skills/evo-app-migrate-react/SKILL.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

evo-react: migrate ebay-menu

3 participants