feat(react-headless-components-preview): add Dropdown component and stories#36101
Open
dmytrokirpa wants to merge 3 commits into
Open
feat(react-headless-components-preview): add Dropdown component and stories#36101dmytrokirpa wants to merge 3 commits into
dmytrokirpa wants to merge 3 commits into
Conversation
📊 Bundle size report
🤖 This report was generated against 0403572a88a8fcd6a1dc7244fdfd21efe0787ec7 |
|
Pull request demo site: URL |
a86fa2b to
edf6790
Compare
…tories Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
edf6790 to
1dab2d3
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new headless Dropdown API surface to @fluentui/react-headless-components-preview (built on @fluentui/react-combobox primitives), along with Storybook examples and Jest/Cypress coverage, and exposes it via a new package export entrypoint.
Changes:
- Added headless
Dropdown,Listbox,Option, andOptionGroupcomponents (plus hooks/renderers/types) and a new./dropdownexport. - Implemented Listbox rendering via the HTML Popover API (
popover+showPopover/hidePopover) and anchored positioning. - Added Storybook stories, CSS module styling for demos, and Jest/Cypress tests for key behaviors.
Reviewed changes
Copilot reviewed 40 out of 40 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/index.stories.tsx | Registers Dropdown stories and docs description. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownDefault.stories.tsx | Default single-select story with clearable behavior. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownMultiselect.stories.tsx | Multiselect story using headless Dropdown APIs. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownControlled.stories.tsx | Controlled selection/value story. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownGrouped.stories.tsx | Grouped options story using OptionGroup. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownControllingOpenAndClose.stories.tsx | Controlled open/close story. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownDisabled.stories.tsx | Disabled trigger story. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/DropdownDescription.md | Component docs description shown in Storybook. |
| packages/react-components/react-headless-components-preview/stories/src/Dropdown/dropdown.module.css | Demo styling and state-driven visuals via data attributes. |
| packages/react-components/react-headless-components-preview/library/src/dropdown.ts | New public entrypoint re-exporting Dropdown-related APIs. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useListboxSlot.ts | Creates Listbox slot and syncs internal open with native popover visibility. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useListboxPopupState.ts | Shared state core for Dropdown/Combobox-like listbox popups (positioning + active-descendant wiring). |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useDropdownContextValues.ts | Bridges Dropdown state into combobox context values. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/useDropdown.ts | Dropdown state construction (trigger slot, clear/expand slots, listbox slot wiring). |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/renderDropdown.tsx | Renders Dropdown with context providers and conditional listbox rendering. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.types.ts | Public Dropdown types based on combobox base hook types plus data attrs. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.tsx | Public Dropdown component wrapper (hook + render + context values). |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/index.ts | Barrel exports for Dropdown and subcomponents. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.test.tsx | Jest tests for basic rendering and data attributes. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Dropdown.cy.tsx | Cypress tests for open/close, selection, keyboard nav, groups, etc. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Listbox/Listbox.tsx | Headless Listbox wrapper component. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Listbox/useListbox.ts | Listbox state hook (sets popover). |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Listbox/useListboxContextValues.ts | Listbox context value derivation. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Listbox/renderListbox.tsx | Re-exported listbox renderer. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Listbox/Listbox.types.ts | Headless listbox types re-exported from combobox. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Listbox/index.ts | Barrel exports for Listbox utilities/types. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Option/Option.tsx | Headless Option wrapper component. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Option/useOption.ts | Option hook adding data-disabled/data-selected. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Option/renderOption.tsx | Re-exported option renderer. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Option/Option.types.ts | Option types and data-attribute augmentation. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/Option/index.ts | Barrel exports for Option utilities/types. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/OptionGroup/OptionGroup.tsx | Headless OptionGroup wrapper component. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/OptionGroup/useOptionGroup.ts | OptionGroup state hook wrapper. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/OptionGroup/renderOptionGroup.tsx | Re-exported option group renderer. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/OptionGroup/OptionGroup.types.ts | OptionGroup types re-exported from combobox. |
| packages/react-components/react-headless-components-preview/library/src/components/Dropdown/OptionGroup/index.ts | Barrel exports for OptionGroup utilities/types. |
| packages/react-components/react-headless-components-preview/library/package.json | Adds @fluentui/react-combobox dependency and ./dropdown export mapping. |
| packages/react-components/react-headless-components-preview/library/etc/dropdown.api.md | API Extractor report for the new dropdown entrypoint. |
| packages/react-components/react-headless-components-preview/library/bundle-size/AllComponents.fixture.js | Includes dropdown entrypoint in bundle-size fixture. |
| change/@fluentui-react-headless-components-preview-17c2b97d-c15d-45a5-a29a-1bcac5b709ad.json | Beachball change file for the new feature. |
Comment on lines
+102
to
+113
| useIsomorphicLayoutEffect(() => { | ||
| const listboxElement = popoverRef.current; | ||
| if (!listboxElement) { | ||
| return; | ||
| } | ||
| if (open) { | ||
| if (!listboxElement.matches(':popover-open')) { | ||
| listboxElement.showPopover(); | ||
| } | ||
| } else if (listboxElement.matches(':popover-open')) { | ||
| listboxElement.hidePopover(); | ||
| } |
Comment on lines
+92
to
+114
| // Sync the native popover state with the internal `open` state. The internal ref captures the | ||
| // listbox DOM element so we can imperatively call `showPopover()` / `hidePopover()`. | ||
| const popoverRef = React.useRef<HTMLDivElement>(null); | ||
| const listboxRef = useMergedRefs(listboxSlot?.ref, ref, popoverRef); | ||
| if (listboxSlot) { | ||
| listboxSlot.ref = listboxRef; | ||
| listboxSlot.onMouseDown = onMouseDown; | ||
| listboxSlot.onClick = onClick; | ||
| } | ||
|
|
||
| useIsomorphicLayoutEffect(() => { | ||
| const listboxElement = popoverRef.current; | ||
| if (!listboxElement) { | ||
| return; | ||
| } | ||
| if (open) { | ||
| if (!listboxElement.matches(':popover-open')) { | ||
| listboxElement.showPopover(); | ||
| } | ||
| } else if (listboxElement.matches(':popover-open')) { | ||
| listboxElement.hidePopover(); | ||
| } | ||
| }, [open]); |
Comment on lines
+144
to
+147
| if (clearable && multiselect) { | ||
| // eslint-disable-next-line no-console | ||
| console.error(`[@fluentui/react-combobox] "clearable" prop is not supported in multiselect mode.`); | ||
| } |
Comment on lines
+36
to
+41
| <Option | ||
| className={styles.option} | ||
| key={option} | ||
| checkIcon={{ className: styles.checkIcon, children: <CheckmarkRegular /> }} | ||
| disabled={option === 'Ferret'} | ||
| > |
| @@ -0,0 +1 @@ | |||
| A Dropdown is a selection component composed of a button and a list of options. The button displays the current selected item or a placeholder, and the list is visible on demand by clicking the button. Dropdowns are typically used in forms. | |||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Dropdowncomponent (withListbox,Option,OptionGroup) toreact-headless-components-previewpopoverattribute forListbox(so noPortalneeded)react-comboboxcovering the behaviors that matter for headless usage: Default, Multiselect, Controlled, Grouped, ControllingOpenAndClose, DisabledStories
multiselectprop — comma-separated value, checkmarks on all optionsvalue+selectedOptions+onOptionSelectfully controlledOptionGroupwith labels and separatorsopen+onOpenChangedriven by an external checkboxdisabledon the trigger buttonScreen.Recording.2026-05-07.at.12.00.53.mov