wbDesign Wiki
Storybook
Для этого компонента пока нет примера в опубликованном Storybook.

Level: molecule.

Purpose

wbl-multiselect lets a user choose several values from a supplied list and shows that controlled selection in one field. Use it for a reusable field outside Filter Row. It filters the supplied local list as the user types. Do not use it for free-form option creation, asynchronous search, or a Filter-Row-specific draft-and-apply selection; those are separate boundaries.

Anatomy

  • Optional label, required marker, placeholder, description, clear action, and opening chevron.
  • A combobox field trigger that becomes an in-field filter on opening, plus an attached checkbox listbox.
  • Text presentation renders the selected labels as a summary.
  • Tag presentation renders each selected label as a closable compact Tag.

Variants and visual states

  • variant: text or tag.
  • size: L (56px), M (44px), or S (32px) when empty. Text keeps those field heights when it is a one-line summary.
  • state: default, active while its listbox is open, and disabled.
  • Switching Text valueLayout changes only the value overflow/wrapping rule. It must preserve the truncate layout's top and bottom content padding, label position, and one-line field height; wrap may add height only after a value actually occupies an additional line.
  • A filled Tag field wraps selected Tags with a 4px gap. The verified two-line heights are 80px (L), 68px (M), and 56px (S); additional wrapping must grow the field rather than clipping selected values.
  • The listbox stays 4px from the field in every size and placement. Clear uses cross-S; its rendered icon size matches the chevron for its field size.

Text summary

  • With valueLayout='truncate', render one line and show no more than maxVisibleItems labels followed by , ещё N for the remaining values. When maxVisibleItems=null, all labels participate in the one-line summary and the suffix is hidden.
  • With valueLayout='wrap', render all selected labels in a wrapping summary, hide , ещё N, and grow the field with the summary while retaining the same visible top and bottom insets as the fixed summary. With one rendered value line, wrap has exactly the same label position and field geometry as truncate; additional height comes only from a new value line.
  • The component counts hidden values from maxVisibleItems; layout width is never used to infer the suffix count.

Tag summary

  • Each selected value is a wbl-tag with size="S", appearance="selected", and closable=true unless the whole Multiselect is disabled.
  • When the Tag field is open, its in-field filter follows the selected Tags in the same wrapping flow. It takes the remaining row space or moves to the next row; it never replaces or overlays a selected Tag.
  • The Tag in-field filter and its placeholder use --typography-description-puma in every size.
  • Closing one Tag removes only that value. Clear removes every selected value.

Behavior

  • items and selectedIds are controlled inputs. selectedIds is the source of truth; the component never retains an uncommitted selection.
  • Selecting or deselecting an option immediately emits the next immutable selectedIds array. There is no Apply action or draft state.
  • The trigger toggles the listbox. Clear and individual Tag removal emit their changes immediately without introducing a draft state or closing the listbox.
  • Opening the field focuses an in-field filter. It matches items by a case-insensitive substring of label; filtering is local, resets when the listbox closes, and never alters selectedIds on its own.
  • Disabled Multiselects neither open nor emit selection, clear, or removal changes. Disabled options remain visible but cannot be toggled.
  • Async loading, remote search, option creation, and Filter Row behavior are intentionally out of scope.

Accessibility

  • The closed trigger has role="combobox", aria-haspopup="listbox", aria-expanded, and aria-controls bound to the overlay listbox. On open, the native filter input owns the combobox semantics and receives focus.
  • The overlay uses listbox semantics, aria-multiselectable="true", and option semantics with aria-selected; it does not use menu semantics.
  • Enter, Space, and ArrowDown open the listbox. In the listbox, ArrowUp and ArrowDown move the active option; Space toggles it; Escape closes and returns focus to the trigger. In the filter input, ArrowDown moves to the first visible option and Escape closes the listbox. Disabled options are skipped by keyboard navigation.
  • Clear and every visible Tag close control have a meaningful accessible name.

Figma source

The Text Figma master exposes Value layout: Fixed / truncate maps to the Angular valueLayout='truncate', while Growing / wrap maps to valueLayout='wrap'. _FieldValue remains private Figma assembly.

The local in-field filter is runtime behavior; it has no separate published Figma variant.

Reusable Figma assembly is recorded in the Multiselect sidecar.

Local component contract

Classes and selectors

  • WblMultiselectComponent — wbl-multiselect (src/app/design-system/patterns/multiselect/multiselect.component.ts).

Public types

  • WblMultiselectVariant = 'text' | 'tag'.
  • WblMultiselectSize = 'L' | 'M' | 'S'.
  • WblMultiselectValueLayout = 'truncate' | 'wrap'.
  • WblMultiselectItem = { id: string; label: string; disabled?: boolean }.

Inputs

  • size = input<WblMultiselectSize>('L')
  • variant = input<WblMultiselectVariant>('text')
  • items = input<readonly WblMultiselectItem[]>([])
  • selectedIds = input<readonly string[]>([])
  • opened = input(false)
  • disabled = input(false) — keeps the field closed and non-interactive: it cannot open the dropdown, render Tag close controls, or emit selection, clear, or Tag-removal changes.
  • label = input('Label')
  • required = input(false)
  • placeholder = input('Placeholder')
  • description = input('')
  • clearable = input(true)
  • valueLayout = input<WblMultiselectValueLayout>('truncate') — Text only.
  • maxVisibleItems = input<number | null>(2) — Text only; null keeps all labels in the one-line summary.
  • ariaLabel = input<string | null>(null)

The local filter is enabled for both variants. It is intentionally internal: the component filters the supplied items by label and has no query input or remote-search API.

Outputs

  • selectedIdsChange = output<string[]>()
  • openedChange = output<boolean>()

Slots and composition

  • The component has no content-projection slot.
  • It composes wbl-dropdown in semanticRole="listbox" and checkbox mode for its overlay. It uses wbl-tag only for the Tag presentation.

Local evidence

  • Code: src/app/design-system/patterns/multiselect/multiselect.component.ts.
  • Focused test: src/app/design-system/patterns/multiselect/multiselect.component.spec.ts.
  • Public export: export * from './patterns/multiselect/multiselect.component'; in src/app/design-system/index.ts.
  • Storybook: storybook/stories/multiselect.stories.ts (Design System/Patterns/wbl-multiselect).
WB Logistics · Design SystemСборка 91da5ae

Поиск по Wiki

Начните вводить название или текст