Skip to content

feat(select,tree-select): master checkbox "select all" (#DS-3969) - #1879

Merged
lskramarov merged 9 commits into
mainfrom
feat/DS-3969
Aug 12, 2026
Merged

feat(select,tree-select): master checkbox "select all" (#DS-3969)#1879
lskramarov merged 9 commits into
mainfrom
feat/DS-3969

Conversation

@lskramarov

Copy link
Copy Markdown
Contributor

No description provided.

Adds a `selectAll` input that renders the "select all" row above the options — until now the
master checkbox only existed hand-rolled inside the filter-bar pipes.

The row acts on the options the user can actually toggle, and its tri-state is derived from the
very same set, so the checkbox and the toggle can never disagree. Under an active search that
means the matches only; for the tree it is the whole data set (collapsed branches included)
without a query, and the visible nodes with one.

Ctrl/Cmd + A becomes a two-way toggle while `selectAll` is on, and both entry points emit a
single `selectionChange` for the whole batch, followed by `onSelectAll`.

Note: `KbqTreeSelection.selectAllOptions` now leaves out the nodes a filter hides, and the
default Ctrl/Cmd + A handler of `kbq-select` no longer sweeps up non-selectable options.
A follow-up review of the select-all master checkbox (added in de0893f) found several
correctness and accessibility gaps: the row went keyboard-inert whenever it held real DOM
focus (KbqOption swallowed Space/Enter before the panel's toggle handler ever saw it),
virtual scroll and preselected values let it silently select only a fraction of the data
while still showing "checked", and the tree's row ignored the component's own `disabled`
state. The keyboard fix also repairs the identical latent bug in filter-bar's pipe rows,
which share the same primitive.

Also fixes a locale-lookup crash on custom locale data, a stale onSelectAll JSDoc, a
shouldShowSearch() render race in tree-select, and adds role="checkbox"/aria-checked to
the row so screen readers can announce its state.

Adds Playwright coverage (including two real-browser keyboard regression tests) and ~20
Jest cases for the gaps above: standalone KbqTreeSelection usage, disabled nodes,
indeterminate+allowDeselect, Ctrl/Cmd+A search-text selection, and keyboard boundaries.

Splits the single combined docs example into basic/search/disabled variants for both
select and tree-select, and expands the "Master checkbox" section to spell out the
tri-state behaviour and indeterminate-click semantics.
@lskramarov lskramarov self-assigned this Aug 11, 2026
Copilot AI lite review requested due to automatic review settings August 11, 2026 11:55
@lskramarov lskramarov added the enhancement New feature or request label Aug 11, 2026
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Visit the preview URL for this PR (updated for commit cd23578):

https://koobiq-next--prs-1879-0zbudodw.web.app

(expires Sat, 15 Aug 2026 13:28:12 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

Sign: c9e37e518febda70d0317d07e8ceb35ac43c534c

@github-actions

Copy link
Copy Markdown

🚨 E2E tests failed

Review the report for details.


💡 Comment /approve-snapshots to approve snapshot changes.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a built-in “Select all” master checkbox row for both kbq-select and kbq-tree-select/kbq-tree-selection, aligning click and Ctrl/Cmd+A behavior, and adding locale support, documentation, examples, and test coverage.

Changes:

  • Added selectAll inputs and master checkbox rows (with tri-state visual state) to KbqSelect and KbqTreeSelection, plus unified keyboard toggling behavior.
  • Introduced shared core utilities/types for select-all state (getSelectAllState) and added select.selectAll to locales.
  • Added/updated unit tests, Playwright e2e coverage, and docs/examples for select-all scenarios (default, search-filtered, disabled items).

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tools/public_api_guard/components/tree.api.md Public API snapshot updates for tree select-all support.
tools/public_api_guard/components/tree-select.api.md Public API snapshot updates for tree-select selectAll input.
tools/public_api_guard/components/select.api.md Public API snapshot updates for select selectAll input and new APIs.
tools/public_api_guard/components/core.api.md Public API snapshot updates for locale/type + getSelectAllState.
tools/cspell-locales/ru.json Adds RU spelling entry used in docs (“виртуализированный”).
packages/e2e/routes.ts Registers new select/tree-select e2e routes.
packages/docs-examples/example-module.ts Adds new docs examples to examples registry + lazy imports.
packages/docs-examples/components/tree-select/tree-select-select-all/tree-select-select-all-example.ts New tree-select “select all” example.
packages/docs-examples/components/tree-select/tree-select-select-all-search/tree-select-select-all-search-example.ts New tree-select “select all + search” example.
packages/docs-examples/components/tree-select/tree-select-select-all-disabled/tree-select-select-all-disabled-example.ts New tree-select “select all + disabled nodes” example.
packages/docs-examples/components/tree-select/index.ts Exports/registers new tree-select examples.
packages/docs-examples/components/select/select-select-all/select-select-all-example.ts New select “select all” example.
packages/docs-examples/components/select/select-select-all-search/select-select-all-search-example.ts New select “select all + search” example.
packages/docs-examples/components/select/select-select-all-disabled/select-select-all-disabled-example.ts New select “select all + disabled options” example.
packages/docs-examples/components/select/index.ts Exports/registers new select examples.
packages/components/tree/tree-selection.component.ts Implements tree “select all” row, state, locale label integration.
packages/components/tree/tree-selection.component.spec.ts Unit tests for tree selection select-all behavior (incl. filtering/disabled/keyboard).
packages/components/tree/tree-option.component.ts Adds selectAllRow handling and level override for synthetic row.
packages/components/tree-select/tree-select.ru.md Documents tree-select master checkbox behavior (RU).
packages/components/tree-select/tree-select.en.md Documents tree-select master checkbox behavior (EN).
packages/components/tree-select/tree-select.component.ts Wires selectAll input into underlying tree + emits onSelectAll.
packages/components/tree-select/tree-select.component.spec.ts Unit tests for tree-select selectAll rendering/state/toggling/keyboard.
packages/components/tree-select/e2e.ts Adds e2e component for tree-select select-all states.
packages/components/tree-select/e2e.playwright-spec.ts Playwright coverage for tree-select select-all visuals + keyboard regression.
packages/components/select/select.ru.md Documents select master checkbox behavior (RU).
packages/components/select/select.html Renders select-all row in the panel template.
packages/components/select/select.en.md Documents select master checkbox behavior (EN).
packages/components/select/select.component.ts Implements select-all row, navigation list, locale label, and batch propagation.
packages/components/select/select.component.spec.ts Unit tests for select selectAll rendering/state/toggling/keyboard + unsupported modes.
packages/components/select/e2e.ts Adds e2e component for select select-all states.
packages/components/select/e2e.playwright-spec.ts Playwright coverage for select select-all visuals + keyboard regression.
packages/components/core/selection/select-all.ts Adds getSelectAllState + strengthens adapter contract docs.
packages/components/core/selection/select-all.spec.ts Unit tests for getSelectAllState and toggle behavior.
packages/components/core/option/option.ts Adjusts keydown handling to not swallow Enter/Space on disabled/non-selectable options.
packages/components/core/locales/types.ts Adds KbqSelectLocaleConfiguration type (shared select/tree selectAll label).
packages/components/core/locales/tk-TM.ts Adds select.selectAll to Turkmen locale.
packages/components/core/locales/ru-RU.ts Adds select.selectAll to Russian locale.
packages/components/core/locales/pt-BR.ts Adds select.selectAll to Portuguese (BR) locale.
packages/components/core/locales/es-LA.ts Adds select.selectAll to Spanish (LA) locale.
packages/components/core/locales/en-US.ts Adds select.selectAll to English (US) locale.
packages/components-dev/tree-select/module.ts Adds new tree-select dev examples to dev page.
packages/components-dev/select/module.ts Adds new select dev examples to dev page.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/components/select/select.component.ts Outdated
@lskramarov

Copy link
Copy Markdown
Contributor Author

/approve-snapshots

@github-actions

Copy link
Copy Markdown

🔄 Updating snapshots.

@github-actions

Copy link
Copy Markdown

✅ Snapshots updated!

Reshapes the "Select all" section to the agreed layout: a short intro stating the feature is
off by default, the plain example, and then a second one showing that the control can display
a label of its own once everything is selected instead of listing every selected value. The
per-behaviour "with search" / "with disabled" examples are dropped — that behaviour is
described in prose instead, so the section stays as short as the design calls for.

The label example projects `kbq-select-trigger` only while `allOptionsSelected` is `true`, so
the control falls back to its default trigger the moment the selection becomes partial.

Doing so surfaced a real defect: `KbqTreeSelection.allOptionsSelected` and `selectAllState`
threw a TypeError when read before the `treeControl` input had been assigned — which is
exactly what a template reference in the trigger does on the pass that assigns it. They now
report an empty state instead, with a regression test covering it.
…idth

The label examples projected the custom trigger through an `<ng-container>`, which produces no
DOM element — so the `kbq-select-trigger { width: 100% }` rule in _select.scss had nothing to
apply to. The label and the chevron ended up bunched together on the left with ~210px of dead
space to the right of the arrow, instead of label-left / arrow-right.

Using the element form fixes it: measured in both dev apps, the arrow now sits flush against
the right edge of the matcher (0px of trailing space) in every state — custom label, default
fallback after a partial selection, and back — matching the untouched reference examples.

Spells the requirement out in the prose too, since projecting an `ng-container` here is an easy
and silent mistake to repeat.
The infix drops its left padding for a non-empty multiple select because the tags carry an
indent of their own. A projected `kbq-select-trigger` replaces the tag list outright, so
nothing was left to supply it and the label sat 6px tighter to the border than a single
select's value — visible in the new "select all" label examples.

Restores the standard infix padding for that case, leaving the tag and placeholder paths
untouched. Measured across both dev apps, every trigger now starts 12px from the control
border: placeholder, tags, single, multiline matcher, and the custom trigger alike.
Addresses the Copilot review comment on #1879.

`localeService.changes` is a BehaviorSubject on a root-provided service, so subscribing
without a teardown kept every destroyed select reachable and let `updateLocaleParams` run —
markForCheck-ing a dead view — on the next locale switch. `KbqSelect` was the only component
in the repo missing the operator; the other 13 call sites, including `KbqTreeSelect` and
`KbqTreeSelection`, already pipe through `takeUntilDestroyed`.

The line predates this branch, but it sits in the block this PR touches and the fix is a
one-liner, so it lands here rather than as a follow-up.

Covered by a test that asserts `getParams('select')` is no longer called after destroy;
reverting the operator makes it fail.
}

/** Rebuilds the key manager's list: the "select all" row first, then the projected options. */
private syncNavigableOptions(): void {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

При открытии с клавиатуры activeItem - всегда второй элемент списка (не "выбрать все"). В tree-select такой проблемы нет

Screen.Recording.2026-08-12.at.10.11.48.mov

Comment thread packages/components/select/select.html Outdated
The row is created together with the overlay, so the highlight `openPanel()`
applies is computed against a navigable list that does not hold it yet and comes
to rest on the option below it. Re-apply the highlight the moment the row joins
the list, from whichever of the view query and `options.changes` brings it in.
`selectAllState` re-filters the whole option list on every read, and the master
checkbox row read it three times plus `allOptionsSelected` — which is the same
walk asking whether the state is `checked`. Hoist it into a `@let` and derive
the selected class from it.
@lskramarov
lskramarov merged commit 327f2ad into main Aug 12, 2026
13 checks passed
@lskramarov
lskramarov deleted the feat/DS-3969 branch August 12, 2026 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants