# Table Header

Level: molecule.

## Purpose and use

Label a table column, request a sort change, or expose the table's select-all
checkbox. Use the checkbox variant only for row selection. Sorting and selected
row state remain controlled by the enclosing table; this component does not
reorder records or calculate aggregate selection. Use a regular heading for
content outside a table.

## Anatomy, behavior, and accessibility

- A `columnheader` contains either a text label with optional decorative info/sort
  icons, or the small Checkbox primitive. A sortable icon is wrapped by the
  shared Tooltip with text «Сортировать». There are no projected content slots.
- A sortable text header uses a native button with a visible keyboard focus
  outline. `aria-sort` reflects the supplied direction. Activating the button
  requests `none → ascending → descending → ascending`; the parent must write
  the emitted direction back. No click returns to `none`.
- The checkbox receives checked, intermediate, disabled and accessible-label
  values. Disabled checkbox changes are ignored. `checkboxLabel` names the
  checkbox; `ariaLabel` (falling back to `label`) names its header container.
- Labels stay on one line with ellipsis. Hovering an overflowing label opens a
  tooltip with the full label; leaving closes it. Prefer short, distinct column
  names. The info icon is decorative and has no help interaction.
- Right alignment moves the sort icon before the label. Every header owns its
  bottom separator. `sticky='left'` adds the inline-end separator and
  `sticky='right'` adds the inline-start separator; positioning and sticky
  offsets belong to Table.

## Open Questions

- `disabled` currently affects the checkbox only; the sortable button remains
  active. A disabled sorting state needs a separate Angular change if required.
- Overflow tooltips are mouse-triggered and are not linked with
  `aria-describedby`; keyboard-triggered overflow help is not implemented.

## Local component contract

This is the active owned contract for `component.table-header`. The API below
is verified against the current local implementation, focused test and story.
The Source commit remains migration provenance, not the runtime owner.

### Classes and selectors

- `WblTableHeaderComponent` — `wbl-table-header` (src/app/design-system/patterns/table-header/table-header.component.ts).

### Inputs

- `WblTableHeaderComponent.alignment = input<WblTableHeaderAlignment>('left')`
- `WblTableHeaderComponent.variant = input<WblTableHeaderVariant>('text')`
- `WblTableHeaderComponent.label = input('Text')`
- `WblTableHeaderComponent.sticky = input<WblTableHeaderSticky>('none')`
- `WblTableHeaderComponent.infoIcon = input(false)`
- `WblTableHeaderComponent.sortable = input(false)`
- `WblTableHeaderComponent.sortDirection = input<WblTableHeaderSortDirection>('none')`
- `WblTableHeaderComponent.checked = input(false)`
- `WblTableHeaderComponent.intermediate = input(false)`
- `WblTableHeaderComponent.disabled = input(false)`
- `WblTableHeaderComponent.ariaLabel = input('')`
- `WblTableHeaderComponent.checkboxLabel = input('Выбрать все строки')`

### Public types and values

- `WblTableHeaderAlignment`: `'left' | 'right'`.
- `WblTableHeaderVariant`: `'text' | 'checkbox'`.
- `WblTableHeaderSticky`: `'none' | 'left' | 'right'`.
- `WblTableHeaderSortDirection`: `'none' | 'ascending' | 'descending'`.

### Models

- None.

### Outputs

- `WblTableHeaderComponent.sortChange = output<WblTableHeaderSortDirection>()`
- `WblTableHeaderComponent.checkedChange = output<boolean>()`

### Slots and projection markers

- The exact template has no Angular content-projection slot.

### Local evidence

- Code: `src/app/design-system/patterns/table-header/table-header.component.ts`.
- Focused test: `src/app/design-system/patterns/table-header/table-header.component.spec.ts`.
- Public export: `export * from './patterns/table-header/table-header.component';` in `src/app/design-system/index.ts`.
- Storybook: `storybook/stories/table-header.stories.ts` (`Design System/Patterns/wbl-table-header`).

## Figma source and verified mapping

- Published component set: [Table Header — `3395:383238`](https://www.figma.com/design/EO9tuCsqwuHL8cZ4Eb2psR/%F0%9F%9A%9B-WB-Logistics-UI-kit?node-id=3395-383238), key
  `825907d7b24368ae1024ab36f267b5bdf063f77a`.
- Verified in `D-021` ([GitLab Issue](https://gitlab.com/polozovdaniel/logistics-2/-/work_items/22)).
- Reusable Figma construction is owned by
  [`table-header.figma.md`](table-header.figma.md). Its stable
  `component.table-header` identity and molecule taxonomy match this contract.
- The owner key and exposed properties were rechecked read-only on 2026-09-05
  during D-025.

The published set has 15 variants: text headers use every combination of
`Alignment=Left|Right`, `State=Default|Hover`, and `Sticky=No|Left|Right`;
selection headers use `Alignment=Left`, `State=Checkbox`, and each `Sticky`
side. Text cells are `256px` wide, checkbox cells are `40px` wide, and every
variant is `40px` high.

### Verified property mapping

- Figma `Alignment` maps to `alignment`.
- Figma `State=Checkbox` maps to `variant='checkbox'`; text is the default local
  variant. Figma `State=Hover` is represented by runtime CSS hover, not a public
  Angular input.
- Figma `Sort` maps to `sortable` and `sortDirection`. The local contract also
  emits `sortChange` for accessible interactive sorting.
- Figma `Sticky=No|Left|Right` maps to `sticky='none'|'left'|'right'` for edge
  separators. The enclosing Table owns sticky positioning, offsets and boundary
  selection. Its column-level `fixedColumn` remains a composition setting; the
  standalone Header no longer exposes the former `fixedColumn` separator flag.

Figma uses `12px` horizontal padding and a `4px` content gap, level-2 surface
`#F6F6F9`, secondary separator `#E0E0EB`, and primary-gray text/icon
`#5F5F6D`. Its source typography is Google Sans Regular `11px/14px`; runtime
uses the owned caption role with matching metrics because Google Sans is not a
runtime font asset.

### Resolved implementation gap

The original D-025 review observed that the standalone header lacked the
published bottom separator while Table supplied it. The current main
implementation resolves this: `table-header.component.scss` owns the bottom
border and both sticky-side borders, and `table.component.scss` no longer adds
the duplicate header border. The earlier proposal to move that separator is
therefore resolved, not pending Angular work. This source review does not claim
an additional visual screenshot verification.

### Documentation migration

D-025 preserves the molecule sibling and inventory link as the canonical assembly
owner. The verified property mapping above reflects the current API. Historical
run paths and digests were not rewritten.

## Provenance

Historical Source commit: `0112cba0779a2d2a4b6c7a17243201694344d70a`.
Current ownership remains the local contract above.

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

## M-002 refinement

Every sortable text header exposes the shared Tooltip with the fixed Russian
copy «Сортировать» on pointer hover and keyboard focus of the sort button. The
tooltip is supplemental to the column label and `aria-sort`; it does not replace
the button's accessible name or change the sort cycle. This current behavior is
aligned with Table UI Kit node
[`9036:58781`](https://www.figma.com/design/EO9tuCsqwuHL8cZ4Eb2psR/?node-id=9036-58781).

## M-002 Transport Requests sorting

The four non-preliminary Transport Requests tabs expose sorting on ID,
creation date and vehicle-submission date. Table Header continues to own the
interactive column button, `aria-sort`, Tooltip and direction cycle; Table and
the screen own numeric/date comparison and ordered rows. Creation date starts
descending. No new Header input or output was added for this screen refinement.

## M-002 sort icon motion

The sort control always reserves its layout space. In the unsorted idle state it
is visually hidden at `opacity: 0` and `scale(0.9)`; header hover or a sorted
state reveals it at `opacity: 1` and `scale(1)` over `--duration-fast` with
`--ease-standard`. The `arrow-up` icon rotates 180 degrees for descending order
with the same duration and easing. These decorative transitions do not delay
`sortChange`, `aria-sort`, focus or the controlled direction update and are
disabled by `prefers-reduced-motion: reduce`. No public Header API is added.
