feat(components): add DropdownMenu component (DS-5055) - #457
feat(components): add DropdownMenu component (DS-5055)#457KamilEmeleev wants to merge 15 commits into
DropdownMenu component (DS-5055)#457Conversation
… styles (DS-5055) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # .storybook/components/Roadmap/data.ts # packages/components/src/components/Popover/PopoverInner.tsx
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThis pull request adds the client-side ChangesDropdownMenu
Estimated code review effort: 4 (Complex) | ~45 minutes Mergeability Score: ⚪ Minimal · up to The component addition is merge-ready after normal checks and review; no actionable merge-blocking risk remains. The public type-contract changes may require owner follow-up for release compatibility. Sequence Diagram(s)sequenceDiagram
participant Consumer
participant DropdownMenu
participant ReactAriaMenu
participant DropdownMenuPopover
participant PopoverInner
Consumer->>DropdownMenu: render trigger and compound menu
DropdownMenu->>ReactAriaMenu: provide menu items and state
ReactAriaMenu->>DropdownMenuPopover: open menu overlay
DropdownMenuPopover->>PopoverInner: provide placement and submenu grouping
PopoverInner-->>Consumer: render menu popover
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Visit the preview URL for this PR (updated for commit 774b341): https://react-koobiq-next--prs-457-uwokp1sa.web.app (expires Tue, 18 Aug 2026 15:37:43 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: fc29847d4a9e5cb1adf458c76a9b681c76e2eeff |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/components/src/components/DropdownMenu/DropdownMenu.stories.tsx (1)
42-53: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
DropdownMenu.Pressabletometa.subcomponents.The compound component exposes
Pressable(seepackages/components/src/components/DropdownMenu/DropdownMenu.tsxlines 45-60), and theSeparatorsstory uses it at line 440. The docs page will not list it.♻️ Proposed change
'DropdownMenu.Autocomplete': DropdownMenu.Autocomplete, 'DropdownMenu.Footer': DropdownMenu.Footer, + 'DropdownMenu.Pressable': DropdownMenu.Pressable, },As per coding guidelines: "Add every slot of a compound component to
meta.subcomponents."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/components/src/components/DropdownMenu/DropdownMenu.stories.tsx` around lines 42 - 53, Add DropdownMenu.Pressable to the meta.subcomponents mapping alongside the other DropdownMenu compound-component slots, using the existing DropdownMenu.Pressable symbol. Keep all current subcomponent entries unchanged.Source: Coding guidelines
packages/components/src/components/DropdownMenu/DropdownMenu.test.tsx (1)
474-474: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd the non-null assertion for the indexed element.
Other clicks in this file use
getItems()[1]!. Line 474 omits it. IfnoUncheckedIndexedAccessis enabled in the TypeScript config,userEvent.clickreceivesElement | undefinedand the type check fails.♻️ Proposed change
- await userEvent.click(screen.getAllByRole('menuitemcheckbox')[0]); + await userEvent.click(screen.getAllByRole('menuitemcheckbox')[0]!);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/components/src/components/DropdownMenu/DropdownMenu.test.tsx` at line 474, Add a non-null assertion to the indexed result of getAllByRole('menuitemcheckbox') in the affected userEvent.click call, matching the existing getItems()[1]! usage and satisfying strict indexed-access typing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/components/src/components/DropdownMenu/DropdownMenu.mdx`:
- Line 51: Update the DropdownMenu documentation by changing “Client side
routing” to “Client-side routing” and removing the trailing period from the
DropdownMenu.Pressable list item so list punctuation is consistent.
In `@packages/primitives/src/components/Textarea/types.ts`:
- Around line 1-3: Restore the public TextareaRef type alias in the Textarea
types module, using the existing ComponentRef import and the Textarea component
symbol, while preserving the TextareaProps re-export.
In `@tools/public_api_guard/react-primitives.api.md`:
- Around line 411-419: Address the breaking public API change involving Input
and Textarea by either adding a changeset for `@koobiq/react-primitives` that
documents the required breaking-version update, or preserving the previous
Input/Textarea props and context contracts while exposing the new RAC types.
Update the relevant public exports and type definitions around Input, Textarea,
InputProps, and InputContext accordingly.
---
Nitpick comments:
In `@packages/components/src/components/DropdownMenu/DropdownMenu.stories.tsx`:
- Around line 42-53: Add DropdownMenu.Pressable to the meta.subcomponents
mapping alongside the other DropdownMenu compound-component slots, using the
existing DropdownMenu.Pressable symbol. Keep all current subcomponent entries
unchanged.
In `@packages/components/src/components/DropdownMenu/DropdownMenu.test.tsx`:
- Line 474: Add a non-null assertion to the indexed result of
getAllByRole('menuitemcheckbox') in the affected userEvent.click call, matching
the existing getItems()[1]! usage and satisfying strict indexed-access typing.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 43ff35a1-1252-4f05-aaa0-c3cf9d548ba6
📒 Files selected for processing (65)
.storybook/components/Roadmap/data.tspackages/components/src/components/DropdownMenu/DropdownMenu.mdxpackages/components/src/components/DropdownMenu/DropdownMenu.stories.tsxpackages/components/src/components/DropdownMenu/DropdownMenu.test.tsxpackages/components/src/components/DropdownMenu/DropdownMenu.tsxpackages/components/src/components/DropdownMenu/__stories__/avatar.webppackages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/DropdownMenuAutocomplete.module.csspackages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/DropdownMenuAutocomplete.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuContent/DropdownMenuContent.module.csspackages/components/src/components/DropdownMenu/components/DropdownMenuContent/DropdownMenuContent.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuContent/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuContent/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuFooter/DropdownMenuFooter.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuFooter/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuFooter/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuHeader/DropdownMenuHeader.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuHeader/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuHeader/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuItem/DropdownMenuItem.module.csspackages/components/src/components/DropdownMenu/components/DropdownMenuItem/DropdownMenuItem.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuItem/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuItem/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuPopover/DropdownMenuPopover.module.csspackages/components/src/components/DropdownMenu/components/DropdownMenuPopover/DropdownMenuPopover.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuPopover/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuPopover/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuSection/DropdownMenuSection.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuSection/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuSection/types.tspackages/components/src/components/DropdownMenu/components/DropdownMenuSubmenuTrigger/DropdownMenuSubmenuTrigger.tsxpackages/components/src/components/DropdownMenu/components/DropdownMenuSubmenuTrigger/index.tspackages/components/src/components/DropdownMenu/components/DropdownMenuSubmenuTrigger/types.tspackages/components/src/components/DropdownMenu/components/index.tspackages/components/src/components/DropdownMenu/index.tspackages/components/src/components/DropdownMenu/intl.tspackages/components/src/components/DropdownMenu/types.tspackages/components/src/components/Input/Input.tsxpackages/components/src/components/Input/types.tspackages/components/src/components/Popover/PopoverGroupContext.tspackages/components/src/components/Popover/PopoverInner.tsxpackages/components/src/components/Popover/types.tspackages/components/src/components/SearchInput/SearchInput.tsxpackages/components/src/components/SearchInput/SearchInputContext.tspackages/components/src/components/Textarea/Textarea.tsxpackages/components/src/components/Textarea/types.tspackages/components/src/components/index.tspackages/components/src/styles/utility.module.csspackages/primitives/src/components/Input/Input.tsxpackages/primitives/src/components/Input/InputContext.tsxpackages/primitives/src/components/Input/types.tspackages/primitives/src/components/TextField/TextField.test.tsxpackages/primitives/src/components/TextField/TextField.tsxpackages/primitives/src/components/TextField/types.tspackages/primitives/src/components/Textarea/Textarea.tsxpackages/primitives/src/components/Textarea/TextareaContext.tsxpackages/primitives/src/components/Textarea/types.tspackages/primitives/src/index.tstools/api-extractor/config.jsontools/public_api_guard/components/DropdownMenu.api.mdtools/public_api_guard/components/Input.api.mdtools/public_api_guard/components/Popover.api.mdtools/public_api_guard/components/Textarea.api.mdtools/public_api_guard/react-primitives.api.md
💤 Files with no reviewable changes (2)
- packages/primitives/src/components/TextField/types.ts
- packages/components/src/components/Textarea/types.ts
|
блокируется скролл при открытии, это особенность реализации? |
There was a problem hiding this comment.
Pull request overview
Adds a new DropdownMenu compound component to @koobiq/react-components, built on React Aria primitives, including support for sections, separators, nested submenus, and searchable menus via Autocomplete. The PR also adjusts primitives and popover behavior to better support menu/search integration and submenu overlay grouping.
Changes:
- Introduced
DropdownMenucomponent (implementation, types, styles, stories, docs, and tests) and added it to the public API extractor config/guards. - Updated primitives to re-export additional RAC menu/autocomplete primitives and refactored
Input/Textareaprimitives to rely onreact-aria-componentsimplementations and contexts. - Enhanced
Popoverto support submenu grouping (groupRefsharing) and addedshouldFlip/ extendedanchorReftyping for overlay positioning.
Reviewed changes
Copilot reviewed 64 out of 65 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/public_api_guard/react-primitives.api.md | Updates primitives API report to reflect new RAC re-exports and context/type changes. |
| tools/public_api_guard/components/Textarea.api.md | Updates components API report after TextFieldProps shape change. |
| tools/public_api_guard/components/Popover.api.md | Updates components API report for new PopoverProps/PopoverInnerProps fields. |
| tools/public_api_guard/components/Input.api.md | Updates components API report after TextFieldProps shape change. |
| tools/public_api_guard/components/DropdownMenu.api.md | Adds API report for the new DropdownMenu component. |
| tools/api-extractor/config.json | Adds DropdownMenu to the API extractor components list. |
| packages/primitives/src/index.ts | Re-exports RAC menu/autocomplete primitives and related contexts/types. |
| packages/primitives/src/components/TextField/types.ts | Removes inputElementType from the primitive TextField base props. |
| packages/primitives/src/components/TextField/TextField.tsx | Refactors TextField to infer element type at runtime and integrate FieldInputContext. |
| packages/primitives/src/components/TextField/TextField.test.tsx | Adds coverage ensuring Autocomplete works with Input/Textarea without an explicit element type prop. |
| packages/primitives/src/components/Textarea/types.ts | Switches TextareaProps typing to RAC TextAreaProps. |
| packages/primitives/src/components/Textarea/TextareaContext.tsx | Replaces custom textarea context with RAC TextAreaContext + useSlottedContext. |
| packages/primitives/src/components/Textarea/Textarea.tsx | Re-exports RAC TextArea as Textarea. |
| packages/primitives/src/components/Input/types.ts | Switches InputProps typing to RAC InputProps. |
| packages/primitives/src/components/Input/InputContext.tsx | Replaces custom input context with RAC InputContext + useSlottedContext. |
| packages/primitives/src/components/Input/Input.tsx | Re-exports RAC Input. |
| packages/components/src/styles/utility.module.css | Adjusts multi-select list-item styling to work for menus (no aria-multiselectable on role="menu"). |
| packages/components/src/components/Textarea/types.ts | Removes inputElementType from omitted TextFieldProps surface. |
| packages/components/src/components/Textarea/Textarea.tsx | Drops inputElementType="textarea" when rendering FormField as={TextField}. |
| packages/components/src/components/SearchInput/SearchInputContext.ts | Introduces a context for SearchInput to allow parent-provided defaults. |
| packages/components/src/components/SearchInput/SearchInput.tsx | Merges SearchInput props from its own context and FieldInputContext for Autocomplete/menu usage. |
| packages/components/src/components/Popover/types.ts | Adds shouldFlip, extends PopoverInnerProps (anchorRef widened, trigger added). |
| packages/components/src/components/Popover/PopoverInner.tsx | Adds submenu grouping support and wires groupRef through usePopover. |
| packages/components/src/components/Popover/PopoverGroupContext.ts | Adds context to share the outermost popover container across a popover group (menu + submenus). |
| packages/components/src/components/Input/types.ts | Removes inputElementType from omitted TextFieldProps surface. |
| packages/components/src/components/Input/Input.tsx | Drops inputElementType="input" when rendering FormField as={TextField}. |
| packages/components/src/components/index.ts | Exports the new DropdownMenu from the components entry point. |
| packages/components/src/components/DropdownMenu/types.ts | Defines public DropdownMenu prop types and exported placement/trigger constants. |
| packages/components/src/components/DropdownMenu/intl.ts | Adds localized strings for dropdown-menu search and empty states. |
| packages/components/src/components/DropdownMenu/index.ts | Adds DropdownMenu barrel exports (root, types, subcomponents). |
| packages/components/src/components/DropdownMenu/DropdownMenu.tsx | Implements compounded DropdownMenu API wiring subcomponents onto the root. |
| packages/components/src/components/DropdownMenu/DropdownMenu.test.tsx | Adds comprehensive behavioral tests for open state, keyboard nav, selection, submenus, and search. |
| packages/components/src/components/DropdownMenu/DropdownMenu.stories.tsx | Adds Storybook stories demonstrating all DropdownMenu features. |
| packages/components/src/components/DropdownMenu/DropdownMenu.mdx | Adds documentation page for DropdownMenu usage and API. |
| packages/components/src/components/DropdownMenu/components/index.ts | Exports DropdownMenu subcomponents. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuSubmenuTrigger/types.ts | Defines props for submenu trigger wrapper. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuSubmenuTrigger/index.ts | Barrel export for submenu trigger. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuSubmenuTrigger/DropdownMenuSubmenuTrigger.tsx | Wraps RAC SubmenuTrigger for component API consistency. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuSection/types.ts | Defines props for sections (incl. optional title). |
| packages/components/src/components/DropdownMenu/components/DropdownMenuSection/index.ts | Barrel export for section. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuSection/DropdownMenuSection.tsx | Implements section rendering with optional header styling and dynamic collection support. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuPopover/types.ts | Defines popover props (placement/offset defaults, flip, non-modal, etc.). |
| packages/components/src/components/DropdownMenu/components/DropdownMenuPopover/index.ts | Barrel export for popover. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuPopover/DropdownMenuPopover.tsx | Implements popover behavior for both menus and submenus using contexts from RAC. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuPopover/DropdownMenuPopover.module.css | Adds sizing/overflow styling for dropdown popover shell/container. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuItem/types.ts | Defines item props including align and data attributes. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuItem/index.ts | Barrel export for item. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuItem/DropdownMenuItem.tsx | Implements dropdown item rendering, submenu chevron, and dev warning for missing textValue. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuItem/DropdownMenuItem.module.css | Styles chevron placement and open-state highlight behavior. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuHeader/types.ts | Defines header props passthrough type. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuHeader/index.ts | Barrel export for header. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuHeader/DropdownMenuHeader.tsx | Wraps RAC Header for component API consistency. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuFooter/types.ts | Defines footer props passthrough type. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuFooter/index.ts | Barrel export for footer. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuFooter/DropdownMenuFooter.tsx | Wraps existing DropdownFooter as a DropdownMenu slot. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuContent/types.ts | Defines menu content props (omitting RAC slot prop). |
| packages/components/src/components/DropdownMenu/components/DropdownMenuContent/index.ts | Barrel export for content. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuContent/DropdownMenuContent.tsx | Implements menu content with default localized empty state and autocomplete-aware messaging. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuContent/DropdownMenuContent.module.css | Styles menu scrolling and empty-state presentation. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/types.ts | Defines autocomplete wrapper props passthrough type. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/index.ts | Barrel export for autocomplete wrapper. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/DropdownMenuAutocomplete.tsx | Wraps RAC Autocomplete and provides search-field defaults via SearchInputContext. |
| packages/components/src/components/DropdownMenu/components/DropdownMenuAutocomplete/DropdownMenuAutocomplete.module.css | Styles the search input wrapper spacing inside the popover. |
| .storybook/components/Roadmap/data.ts | Marks DropdownMenu as done and experimental in the roadmap. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Так работает react-aria. Меню открывается поверх страницы как модальное окно — поэтому скролл и блокируется. Отключить блокировку отдельно нельзя. Если сделать меню немодальным, скролл заработает, но меню будет закрываться при прокрутке — react-aria не умеет двигать его за кнопкой. Так же ведут себя наши Menu и SelectNext. |


Summary by CodeRabbit