# Filter Row

Level: organism.

## Purpose and use

Group table filtering, optional search, switches and table actions in one toolbar.
Use this composition for a record list; it does not fetch or filter records itself.

## Current local anatomy, behavior, and accessibility

- The independently wrapping left region contains optional search, projected
  `[wblFilterRowControl]` controls, generated Filter Chips, `+ Фильтр`, and
  reset. The shrink-to-fit right region contains switches, column settings, and
  actions; it does not move when the left region wraps. Add Filter excludes IDs
  already present and keeps its trigger visible while its menu is open.
- Child actions emit their item context; the parent owns filters, search and
  switches. One more-action item becomes a direct icon button; multiple items
  use Dropdown. Icon-only actions have labels and tooltips.
- Reset is visible only when it is enabled. For projected controls, the parent
  must pass `resetDisabled=false` when a screen-owned filter differs from its
  initial state; the `null` default keeps reset disabled and hidden. The
  visible label is «Сбросить фильтры».
- The container is a named toolbar. Search has an accessible name and clear
  action; switches retain their labels. Use feature-specific labels and replace
  default fixture filters/actions. showSecondSwitch adds the built-in problem
  switch only when exactly one configured switch exists.

## Lifecycle, layout, and controlled interaction

- The Row uses the explicit shared
  types `WblFilterLifecycle = 'required' | 'pinned' | 'optional'` and
  `WblFilterAction = 'clear' | 'reset' | 'remove'`. No compatibility aliases
  remain.
- `WblFilterRowFilter` keeps its existing label/value/count content
  fields and adds required `lifecycle` and controlled `opened`; it may carry
  `invalid`, `errorText`, and `disabled`. A Row maps each Chip `openRequest` to
  `filterOpenRequest({ filter, opened })` and each Chip action to
  `filterAction({ filter, action })`. The parent still owns values, dropdown
  overlays, and data filtering.
- A required filter stays visible and its close action emits `clear`; its screen
  may mark an empty value invalid, using the default error text «Выберите
  значение». A pinned filter stays visible and emits `reset` so its screen
  restores the screen-owned default. An optional filter emits `remove`, which
  clears and removes it.
- `+ Фильтр` emits the selected available item. The screen creates an optional
  control and sets its `opened` state. An optional filter closed without a value
  is removed by that screen. Single/live controls apply immediately; multi,
  date, and range controls retain their existing draft and Apply ownership in
  the relevant dropdown wrapper.

### Layout, reset, and accessibility

- The left filter region wraps Chips, `+ Фильтр`, and reset independently with
  `--space-200`; the right switch/columns/actions region is shrink-to-fit and
  must not wrap or move when the left region wraps. The regions are separated by
  `--space-900`. The panel height must not jump as Chips change rows.
- Right region (A-003, Figma `21003:107046` «right side»): each group —
  switches, columns, actions — starts with a 1×20 divider
  (`--color-stroke-secondary`, `--space-500` tall), then `--space-200`, then
  its controls; groups are `--space-200` apart. Download and «Ещё» sit flush
  (no gap), as `CTA1` and `more` in Figma.
- `+ Фильтр` uses the owned secondary Button treatment. Reset sits in the left
  region after a divider, has the exact label «Сбросить фильтры», and is visible
  only after the parent enables it. Screen reset restores pinned filters,
  removes optional filters, and closes open overlays.
- Chip open state is controlled by `opened`; the Row does not infer a popup from
  a native click. Error state exposes `aria-invalid`, an accessible error
  description, and the «Выберите значение» tooltip on hover and keyboard focus.

## Open Questions

The toolbar has no group-level arrow-key navigation. It does not define search
submission, download behavior or column-settings content; these remain screen
integration responsibilities.

## Local component contract

This is the active owned contract for `component.filter-row`. 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

- `WblFilterRowComponent` — `wbl-filter-row` (src/app/design-system/patterns/filter-row/filter-row.component.ts).

### Inputs

- `WblFilterRowComponent.filters = input<WblFilterRowFilter[]>(DEFAULT_FILTERS)`
- `WblFilterRowComponent.addFilterItems = input<WblDropdownItem[]>([])`
- `WblFilterRowComponent.showSearch = input(false, { transform: booleanAttribute })`
- `WblFilterRowComponent.searchLabel = input('Поиск')`
- `WblFilterRowComponent.searchPlaceholder = input('Search')`
- `WblFilterRowComponent.searchValue = input('')`
- `WblFilterRowComponent.showAddFilter = input(true)`
- `WblFilterRowComponent.addFilterLabel = input('Фильтр')`
- `WblFilterRowComponent.showReset = input(true)`
- `WblFilterRowComponent.resetDisabled = input<boolean | null>(null)`
- `WblFilterRowComponent.showSwitchColumn = input(true)`
- `WblFilterRowComponent.showSecondSwitch = input(false)`
- `WblFilterRowComponent.switches = input<WblFilterRowSwitch[]>(DEFAULT_SWITCHES)`
- `WblFilterRowComponent.showColumns = input(true)`
- `WblFilterRowComponent.columnsLabel = input('Настроить колонки')`
- `WblFilterRowComponent.showActions = input(true)`
- `WblFilterRowComponent.showMoreAction = input(true)`
- `WblFilterRowComponent.downloadLabel = input('Скачать')`
- `WblFilterRowComponent.moreLabel = input('Ещё')`
- `WblFilterRowComponent.moreActionItems = input<WblDropdownItem[]>(DEFAULT_MORE_ACTION_ITEMS)`
- `WblFilterRowComponent.downloadDisabled = input(false)`
- `WblFilterRowComponent.moreDisabled = input(false)`
- `WblFilterRowComponent.ariaLabel = input('Фильтры')`

### Public types and allowed values

```ts
type WblFilterRowFilter = {
  id: string;
  label: string;
  value?: string;
  filled?: boolean;
  multiple?: boolean;
  count?: string | number;
  lifecycle: WblFilterLifecycle;
  opened: boolean;
  invalid?: boolean;
  errorText?: string;
  disabled?: boolean;
};
type WblDropdownItem = {
  id: string;
  label: string;
  iconName?: WblIconName;
  imageSrc?: string;
  imageAlt?: string;
  badge?: string;
  rightText?: string;
  selected?: boolean;
  disabled?: boolean;
  separatorBefore?: boolean;
};
type WblFilterRowSwitch = {
  id: string;
  label: string;
  selected?: boolean;
  disabled?: boolean;
};
type WblFilterRowSwitchChange = {
  switchItem: WblFilterRowSwitch;
  selected: boolean;
};
type WblFilterLifecycle = 'required' | 'pinned' | 'optional';
type WblFilterAction = 'clear' | 'reset' | 'remove';
type WblFilterRowOpenRequest = {
  filter: WblFilterRowFilter;
  opened: boolean;
};
type WblFilterRowFilterAction = {
  filter: WblFilterRowFilter;
  action: WblFilterAction;
};
```

`filterOpenRequest = output<WblFilterRowOpenRequest>()` and
`filterAction = output<WblFilterRowFilterAction>()` replace the former click
and removal notifications. `resetClick` remains the Row-level notification for
resetting the full filter set.

Icon-name inputs use `WblIconName` from the [shared icon contract](../atoms/icon.md);
choose a key present in the owned icon pack, not an arbitrary external icon name.

### Models

- None.

### Outputs

- `WblFilterRowComponent.searchValueChange = output<string>()`
- `WblFilterRowComponent.filterOpenRequest = output<WblFilterRowOpenRequest>()`
- `WblFilterRowComponent.filterAction = output<WblFilterRowFilterAction>()`
- `WblFilterRowComponent.addFilterSelect = output<WblDropdownItem>()`
- `WblFilterRowComponent.resetClick = output<void>()`
- `WblFilterRowComponent.switchChange = output<WblFilterRowSwitchChange>()`
- `WblFilterRowComponent.columnsClick = output<void>()`
- `WblFilterRowComponent.downloadClick = output<void>()`
- `WblFilterRowComponent.moreActionSelect = output<WblDropdownItem>()`

### Slots and projection markers

- `WblFilterRowComponent: <ng-content select="[wblFilterRowControl]">`

### Local evidence

- Code: `src/app/design-system/patterns/filter-row/filter-row.component.ts`.
- Focused test: `src/app/design-system/patterns/filter-row/filter-row.component.spec.ts`.
- Public export: `export * from './patterns/filter-row/filter-row.component';` in `src/app/design-system/index.ts`.
- Storybook: `storybook/stories/filter-row.stories.ts` (`Design System/Patterns/wbl-filter-row`). This is the shared active Storybook story for this family.

## Figma status

The inventory-linked [Figma assembly sidecar](wbl-filter-row.figma.md) owns
verified reusable assembly rules. It records the current-node evidence and the
unresolved published Chip/master-key gap; Angular behavior must not fill that
gap by inference.

## Provenance

The archived source-document copy is historical evidence only. The active
resolver is local code/test/export and the exact Storybook story above.
