Level: molecule.
Purpose and use
Present actions, one selection or a set of checkbox selections beside a trigger. Use a filter wrapper for Apply/Clear drafts or searchable choices.
Anatomy, behavior, and accessibility
- The
[wblDropdownTrigger]slot anchors a CDK overlay. The panel contains menu rows with optional icons/images, side text, badges and separators, or a loader. - With the default
controlled=false, trigger activation toggles local open state and emitsopenedChange; backdrop click and overlay detach close it. Withcontrolled=true,openedis the sole overlay-state source: trigger, backdrop/detach, Escape, and selection close interactions only emit the next requested value throughopenedChange. The overlay changes state only after the parent writes that value toopened; duplicate requests for the current parent value are not emitted. The component appliesaria-haspopup,aria-expanded, and, when present,aria-controlsto the native trigger. Default/right placement use viewport fallback positions; nontext trigger width andminPanelWidthbound panel width. - Default/radio choices emit
itemSelectand close; radio also emits the ID. Checkbox choices emit the next ID array and keep the menu open. The parent writes selection back. Empty selectedIds/null selectedId fall back to per-itemselected; avoid conflicting sources when clearing a selection. - Default menu rows use menuitem/menuitemcheckbox/menuitemradio semantics;
semanticRole='listbox'uses options. Opening focuses the selected enabled row or first enabled row. ArrowUp/ArrowDown, Home/End, Escape, Enter and Space work for both overlay semantics; Escape and keyboard selection return focus to the native trigger. Disabled rows cannot select. Loading replaces rows and sets busy. Supply a native named trigger and clear, unique labels/IDs. - The panel itself owns vertical scrolling with
overflow-y: autoandoverscroll-behavior: contain. Maximum heights are26.75remfor L,19.875remfor M, and15.625remfor S.
Local component contract
This is the active owned contract for component.dropdown. The API below
is verified against the current local implementation, focused test and story.
The Source commit 0112cba0779a2d2a4b6c7a17243201694344d70a remains
migration provenance, not the runtime owner.
Classes and selectors
WblDropdownComponent—wbl-dropdown(src/app/design-system/patterns/dropdown/dropdown.component.ts).
Inputs
WblDropdownComponent.size = input<WblDropdownSize>('L')WblDropdownComponent.state = input<WblDropdownState>('default')WblDropdownComponent.itemsType = input<WblDropdownItemsType>('default')WblDropdownComponent.semanticRole = input<WblDropdownSemanticRole>('menu'), whereWblDropdownSemanticRole = 'menu' | 'listbox'WblDropdownComponent.items = input<WblDropdownItem[]>([])WblDropdownComponent.opened = input(false)WblDropdownComponent.controlled = input(false)— opt-in parent-controlled overlay mode; when true, the parent must updateopenedin response toopenedChangefor the requested state to take effect.WblDropdownComponent.hasBackdrop = input(true)WblDropdownComponent.placement = input<WblDropdownPlacement>('default')WblDropdownComponent.panelId = input<string | null>(null)— optional stable overlay id; when provided, the component binds it to the native trigger'saria-controls.WblDropdownComponent.minPanelWidth = input<number | null>(null)WblDropdownComponent.selectedIds = input<readonly string[]>([])WblDropdownComponent.selectedId = input<string | null>(null)
Public types and allowed values
type WblDropdownSize = 'L' | 'M' | 'S';
type WblDropdownState = 'default' | 'loading';
type WblDropdownItemsType = 'default' | 'checkbox' | 'radio';
type WblDropdownSemanticRole = 'menu' | 'listbox';
type WblDropdownItem = {
id: string;
label: string;
iconName?: WblIconName;
imageSrc?: string;
imageAlt?: string;
badge?: string;
rightText?: string;
selected?: boolean;
disabled?: boolean;
separatorBefore?: boolean;
};
type WblDropdownPlacement = 'default' | 'right';
Icon-name inputs use WblIconName from the shared icon contract;
choose a key present in the owned icon pack, not an arbitrary external icon name.
Models
- None.
Outputs
WblDropdownComponent.openedChange = output<boolean>()— the local state change in default mode, or the requested nextopenedvalue in controlled mode.WblDropdownComponent.itemSelect = output<WblDropdownItem>()WblDropdownComponent.selectedIdsChange = output<string[]>()WblDropdownComponent.selectedIdChange = output<string | null>()
Slots and projection markers
WblDropdownComponent: <ng-content select="[wblDropdownTrigger]">
Semantic role and keyboard behavior
semanticRole='menu'is the default and preserves menu and menu-item semantics.semanticRole='listbox'is the narrow composition mode for selection fields. In checkbox mode the panel exposesrole="listbox"witharia-multiselectable="true"; each enabled checkbox item is anrole="option"witharia-selected. It does not expose a nested interactive checkbox to assistive technology.- Opening focuses the selected enabled option or the first enabled row. In both modes ArrowUp/ArrowDown wrap through enabled rows; Home/End move to their bounds; Escape closes and restores focus; Enter/Space activate the row. This does not introduce draft selection, search, or a new selection data API.
Local evidence
- Code:
src/app/design-system/patterns/dropdown/dropdown.component.ts. - Focused test:
src/app/design-system/patterns/dropdown/dropdown.component.spec.ts. - Public export:
export * from './patterns/dropdown/dropdown.component';insrc/app/design-system/index.ts. - Storybook:
storybook/stories/dropdown.stories.ts(Design System/Patterns/wbl-dropdown).
Figma status
This runtime contract does not infer a Figma owner. Assembly evidence, when verified, is maintained separately through the inventory-linked sidecar.
M-002 scrollbar treatment
The Dropdown panel uses a native 4px scrollbar: scrollbar-color pairs the
tertiary thumb with a transparent track and scrollbar-width: thin provides the
fallback. WebKit uses --space-100 width, a transparent track/corner, the
tertiary thumb, and --radius-pill. The thumb is visible whenever content
overflows; it is not hover-only. This styling adds no public API.
Provenance
The archived source-document copy is historical evidence only. The active resolver is local code/test/export and the exact Storybook story above.