Add Dropdown (menu) component#16
Conversation
Compound menu built on @base-ui/react/menu: Dropdown, DropdownTrigger, DropdownContent (Portal+Positioner+Popup), DropdownItem, DropdownCheckboxItem, DropdownGroup, DropdownLabel, DropdownSeparator. Item variant axis default | with-description | with-value (Figma 27-1057); selected/disabled are primitive state. propel tokens via cva+cx, no className/style props.
|
📚 Storybook preview: https://pr-16-propel-storybook.vamsi-906.workers.dev |
There was a problem hiding this comment.
Pull request overview
Adds a new Dropdown (menu) compound component to @plane/propel, built on @base-ui/react/menu and styled with Propel semantic tokens for consistent menu behavior + visuals across the design system.
Changes:
- Introduces
Dropdowncompound primitives (DropdownTrigger,DropdownContent,DropdownItem,DropdownCheckboxItem,DropdownGroup,DropdownLabel,DropdownSeparator). - Implements item layout variants (
default,with-description,with-value) and shared menu surface styling/positioning. - Adds Storybook stories with play tests for default selection (closes) and checkbox multi-select (stays open).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/propel/src/components/dropdown/index.tsx | Implements the new Dropdown compound component API and styling. |
| packages/propel/src/components/dropdown/dropdown.stories.tsx | Adds Storybook documentation + play tests for the Dropdown component. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- stories: assert inside waitFor callback (querySelector null resolves immediately) - stories: add DropdownGroup to subcomponents (all exported parts listed) - DropdownCheckboxItem: disabled styling on check indicator + aria-hidden on icon
Multi-select rows now render the propel Checkbox as the leading control (via a new presentational CheckboxVisual, so the menuitemcheckbox row stays the single interactive control and ARIA-valid). Single-select rows show a leading checkmark on the selected row only. New compound parts: DropdownSearch (sticky search header, outside role=menu), DropdownFooter, DropdownSub/DropdownSubTrigger/DropdownSubContent (submenu), plus DropdownItem slots (leading control, selected checkmark, secondaryText, trailing badge/shortcut) and a DropdownContent width axis. All documented in the stories' subcomponents. Adds one real, interactive story per Figma demo (12): Status, Labels, ActionMenu, Description, Assignees, LanguagePicker, Priority, Filters, DisplayProperties, DisplayAccordion, EmptyState, Submenu — each with an open/select play test. Composes Checkbox/Radio/Avatar/Badge. vp check + run -r test (59 passed) + run -r build (attw clean) all pass.
Reworked: Checkbox integration + one interactive story per Figma demoCheckbox integration (multi-select)Multi-select rows ( The 12 demos to stories (each interactive, with an open/select play test)
New compound parts (all in
|
Drop defaultVariants from both cva blocks in dropdown/index.tsx. The essential item layout axis (variant: default/with-description/with-value) is now a required prop; the additive surface width axis stays optional with no default. Update all 12 demo stories to pass variant explicitly.
|
Removed defaultVariants; item variant now required. |
In RTL submenus open to the inline-start (left) side; flip the DropdownSubTrigger ChevronRight so it points toward the submenu. Rest of the component already uses logical utilities (gap, px, -mx), so no other physical-direction conversions were needed.
RTL fixThe submenu trigger
Directional-utility scanScanned the whole component (items, search, footer, sub-parts, checkmark/value/trailing slots). It was already RTL-clean: everything uses logical/symmetric utilities ( VerificationRendered in Storybook wrapped in Base UI
Gates: |
|
…i, new-label, filters, pills) - Item icon top-aligned (align-start) so it sits with the first line on multi-line (with-description) rows; Archive in ActionMenu now top-aligns. - Selected single-select rows keep their own icon and mark selection with a trailing check (no longer replace the leading icon with a tick). - Surface (content/panel) radius rounded-lg; menu item radius rounded-md; items 34px tall. Search keeps no radius. - Sub-text/description stays text-tertiary. - Labels demo: 'Add label "…"' option (Figma 64-626) with two divider lines between the search and the new-label item. - Filters demo: divider between categories, leading icons per item, a collapse/expand chevron on each category heading (heading is a menuitem so it stays ARIA-valid); 'View all' uses new emphasis="link" (no hover bg, cursor-pointer). - DisplayAccordion: items within a category sit flush (0 spacing). - Avatars in dropdown items use 2xs magnitude. - DisplayProperties pills restyled to Figma 56-366 (real Pills component pending).
|
@bhaveshraja, all the feedback is addressed. Everything below is live in Storybook, best way to check is to open each story and compare against Figma.
Visually checked Description, ActionMenu, Labels, Filters, DisplayProperties, and DisplayAccordion against Figma. A short list of design questions is in the next comment. |
|
@bhaveshraja, a few quick design questions so I can finish these off:
|
|
Component
Dropdown, a compound menu built on
@base-ui/react/menu, styled with @plane/propel semantic tokens (Tailwind v4 utilities,cva+cx, noclassName/styleprops, no arbitrary hex).Parts / subcomponents
Dropdown(=Menu.Root), open state + trigger/content wiringDropdownTrigger, opens the menu; renders a<button>, projectable viarenderDropdownContent, bundlesPortal+Positioner+Popup(surface-1 bg, overlay shadow, subtle border, scale/fade transition); exposesside/sideOffset/alignDropdownItem, selectable rowDropdownCheckboxItem, toggleable row with a leading check indicator (multi-select; stays open)DropdownGroup(=Menu.Group) +DropdownLabel(=Menu.GroupLabel), titled groupsDropdownSeparator, dividerThe stories declare these via
subcomponents, matching the AvatarGroup pattern.Item variants
variant:default|with-description|with-value(from Figma node 27-1057). An item is optional leading icon/checkbox + label + optional description + optional trailing value/end icon, all content.selected/disabledare primitive state, not variants.Stories
Default, trigger + menu with icons, a trailing value, awith-descriptionitem, a separator, and a disabled item; play test opens the menu (role="menu"), asserts items, and confirms selecting closes it.WithCheckboxes, multi-select viaDropdownCheckboxItem; play test toggles a checkbox and confirms the menu stays open.expectfromstorybook/test,{ canvas, userEvent, waitFor }, queried by role; portal awaited withwaitFor.Verification
vp install, okvp check(format + lint + types), passvp run -r test(Storybook play tests, chromium), 14 passedvp run -r build, pass,attwNo problems found (pre-existingpublint./hooks/*warnings are unrelated)Follow-up
The Figma file also contains a Search-Dropdown / combobox surface. Select is intended as a separate follow-up component.
Dependencies
Blocks: #15 (Table, editable cells are built on the Dropdown).