Skip to content

Commit 17bc91d

Browse files
authored
feat: angular 20 (#DS-5079) (#1585)
Co-authored-by: lskramarov <9027254+lskramarov@users.noreply.github.com>
1 parent 96b8ed7 commit 17bc91d

943 files changed

Lines changed: 12588 additions & 19053 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
dist
22
node_modules
3+
coverage
34

45
# eslint ignores root level "dot" files by default
56
!/.*.js

.eslintrc.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,17 @@ const templateRules = {
242242
'@angular-eslint/template/no-interpolation-in-attributes': 0,
243243
'@angular-eslint/template/no-any': 0,
244244
'@angular-eslint/template/prefer-static-string-properties': 0,
245-
'@angular-eslint/template/cyclomatic-complexity': 0
245+
'@angular-eslint/template/cyclomatic-complexity': 0,
246+
// Allow combining a static `class`/`style` attribute with its `[class]`/`[style]` binding.
247+
// Angular merges them via styling precedence, so this is a valid pattern (e.g. after the
248+
// NgClass -> [class] migration). Genuine duplicates (two static `class`, two `[class]`) are
249+
// still reported.
250+
'@angular-eslint/template/no-duplicate-attributes': [
251+
2,
252+
{
253+
allowStylePrecedenceDuplicates: true
254+
}
255+
]
246256
}
247257
};
248258

@@ -435,7 +445,11 @@ const toolsRules = {
435445
files: ['tools/**/*.ts', 'tools/**/*.js'],
436446
rules: {
437447
// plugin:eslint
438-
'no-console': 0
448+
'no-console': 0,
449+
450+
// plugin:@typescript-eslint
451+
// node-only build scripts may use require() for legacy CommonJS modules
452+
'@typescript-eslint/no-require-imports': 0
439453
}
440454
};
441455

@@ -461,7 +475,11 @@ const cliRules = {
461475
files: ['packages/cli/**/*.ts', 'packages/cli/**/*.js'],
462476
rules: {
463477
// plugin:eslint
464-
'no-console': 0
478+
'no-console': 0,
479+
480+
// plugin:@typescript-eslint
481+
// node-only CLI scripts may use require() for legacy CommonJS modules
482+
'@typescript-eslint/no-require-imports': 0
465483
}
466484
};
467485

.github/workflows/actions/build-packages/entrypoint.sh

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ fi
88
# Fail fast on errors, unset variables, and failures in piped commands
99
set -Eeuo pipefail
1010

11-
echo "::group:: Build CDK ..."
12-
yarn run build:cdk
13-
echo '::endgroup::'
14-
1511
echo "::group:: Build Components ..."
1612
yarn run build:components
1713
echo '::endgroup::'

.github/workflows/units.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ jobs:
1818
# Build styles which are required for unit tests
1919
- run: yarn run styles:build-all
2020
- run: |
21-
yarn run unit:cdk
2221
yarn run unit:components
2322
yarn run unit:angular-moment-adapter
2423
yarn run unit:angular-luxon-adapter

.stylelintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
dist
22
node_modules
3+
coverage
34

45
# ignore auto-generated theme
56
apps/docs/src/styles/koobiq/default-theme

AGENTS.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ yarn install
1212

1313
```
1414
packages/
15-
├── cdk/ # Component Development Kit (a11y, keycodes, testing utils)
16-
├── components/ # Main library - each subdirectory is a component
15+
├── components/ # Main library - each subdirectory is a component (a11y, keycodes, testing utils now live in core)
1716
├── components-experimental/ # Experimental components (not production-ready)
1817
├── docs-examples/ # Documentation examples and components usage
1918
├── components-dev/ # Dev apps for each component (yarn run dev:<component-name>)
@@ -48,7 +47,6 @@ packages/components/button/
4847

4948
```bash
5049
yarn run build:components # Build main components library
51-
yarn run build:cdk # Build CDK utilities
5250
yarn run build:components-experimental # Build experimental components
5351
yarn run build:angular-luxon-adapter # Build Luxon date adapter
5452
yarn run build:angular-moment-adapter # Build Moment date adapter
@@ -74,7 +72,6 @@ There are two types of test files per component:
7472
```bash
7573
# Unit tests (Jest)
7674
yarn run unit:components # Run component unit tests
77-
yarn run unit:cdk # Run CDK unit tests
7875
yarn run unit:schematics # Run schematics tests
7976
npx jest <TEST_PATH_PATTERN> # Run specific Jest tests (e.g., npx jest packages/components/button/button.component.spec.ts)
8077

CHANGELOG.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,155 @@
1+
# 20.0.0 (2026-05-13)
2+
3+
### BREAKING CHANGES — Angular 20
4+
5+
* **angular:** bump to Angular 20.3.21. `requiredAngularVersion` is now `^20.0.0`. All `peerDependencies` of published packages target `^20.0.0`. Consumers must upgrade to Angular 20+.
6+
* **node:** minimum supported Node.js is now **20.19** (added `"engines": { "node": ">=20.19" }`).
7+
* **zone.js:** downgraded to `~0.15.1` to match Angular 20 peer requirement.
8+
* **components:** `KbqPopUpTrigger` (base of tooltip / popover / notification-center / app-switcher) refactored to use `Signal<T>` for `enterDelay`, `leaveDelay`, `stickToWindow`, `container`, `hideWithTimeout`, `preventClose`, and `arrow` so the existing signal-input overrides in subclasses now type-check. Consumers reading these from the trigger instance must call them as signals (`.enterDelay()`, etc.).
9+
* **popover:** the runtime-broken `set trigger(value)` mutation of `hideWithTimeout`/`leaveDelay` (added in DS-3677 #851 and silently broken since DS-4749 #1442) is replaced by writable backing signals; `leaveDelay` is now a `computed` signal combining the user input and the hover-mode fallback default of `500ms`.
10+
* **form-field:** `KbqInput.placeholder`, `KbqInput.errorStateMatcher`, `KbqInputPassword.placeholder`, `KbqInputPassword.errorStateMatcher`, `KbqTextarea.placeholder`, `KbqTextarea.errorStateMatcher`, `KbqSelect.errorStateMatcher`, `KbqTagList.errorStateMatcher`, `KbqTreeSelect.errorStateMatcher`, `KbqSingleFileUploadComponent.errorStateMatcher`, `KbqMultipleFileUploadComponent.errorStateMatcher`, `KbqTagInput.placeholder`/`id` reverted to `@Input` primitives — they conflict with interface types (`CanUpdateErrorState`, `KbqFormFieldControl`, `KbqTagTextControl`) that expect raw values. Template bindings via aliases stay the same.
11+
* **code-block:** `softWrap`, `viewAll`, `canDownload`, `activeFileIndex` are now `model()` signals (writable + bindable). Bare attribute shorthand `<kbq-code-block canDownload>` no longer coerces — use `[canDownload]="true"`, `[activeFileIndex]="1"`, etc.
12+
* **textarea:** `freeRowsHeight` is now a `model()` signal; bare attribute shorthand is not supported, use `[freeRowsHeight]="160"`.
13+
* **search-expandable / dl / radio / checkbox / toggle / sidepanel:** `isOpened` / `vertical` / `name` / `id` / `sidepanelResult` are now `model()` signals (use `.set()` / `.update()` internally; two-way binding `[(x)]` works externally).
14+
* **modal:** `kbqOkText`, `kbqOkType`, `kbqRestoreFocus`, `kbqCancelText`, `kbqModalType`, `kbqComponent`, `kbqContent`, `kbqComponentParams`, `kbqFooter`, `kbqWidth`, `kbqSize`, `kbqWrapClassName`, `kbqClassName`, `kbqStyle`, `kbqTitle`, `kbqCloseByESC`, `kbqOnOk`, `kbqOnCancel` reverted to plain `@Input`/`@Output` to match `ModalOptions`. Programmatic API (consumed via `KbqModalService`) stays the same.
15+
* **notification-center / app-switcher:** removed `placement` signal-input overrides (which conflicted with `KbqPopUpTrigger.placement`) — placement is again configured via `@Input('kbqNotificationCenterPlacement')` / `@Input('kbqAppSwitcherPlacement')` getter/setter pattern delegating to `super.updatePlacement(...)`. `arrow` in both is now a `Signal<boolean>` (matches the new base contract).
16+
* **tabs:** `KbqPaginatedTabHeader.disablePagination` is now a `computed` that combines the user input with a writable fallback set by the `vertical` setter. `KbqTabGroupComponent.animationDuration` is a `computed` that resolves user input → `KBQ_TABS_CONFIG` default → `'0ms'`. `KbqTabLink.disabled` reverted to plain `@Input` (matches `FocusableOption.disabled`).
17+
* **dropdown:** `KbqDropdown.backdropClass` reverted to `@Input` to match the `KbqDropdownPanel` interface. `KbqDropdownContent` constructor no longer accepts `ComponentFactoryResolver` (removed in Angular 20).
18+
* **input-number:** dropped `@Attribute('step' | 'big-step' | 'min' | 'max')` constructor coercion; the same defaults are now provided by `input(..., { transform: numberAttribute })`. Template bindings `[step]="..."` still work; bare HTML attributes are coerced through the transform.
19+
* **breadcrumbs:** `RdxRovingFocusGroupDirective.orientation` is a `computed` signal combining an `orientation` input alias and an internal `setOrientation()` override called by `KbqBreadcrumbs` (replaces the broken `inject(...).orientation = 'horizontal'` assignment).
20+
* **navbar / navbar-ic / filter-bar / datepicker / timepicker / splitter:** direct assignments to readonly signal inputs (`this.arrow = false`, `this.offset = 0`, `this.popover.preventClose = true`, `tooltip.enterDelay = ...`, `this.ghost.visible = ...`, etc.) cast to `any` to preserve current runtime behavior. The underlying state migration to writable signals is tracked as a follow-up.
21+
22+
### Tooling
23+
24+
* `ng-packagr``^20.3.2`.
25+
* `@angular-builders/jest``20.0.0`.
26+
* `@angular-eslint/*``^20.7.0`.
27+
* `@typescript-eslint/*``^8.59.3` (ESLint stays on `8.57.1`).
28+
* `@schematics/angular``20.3.21` (was stuck on `18.2.21`).
29+
* `@angular-devkit/architect``0.2003.21`.
30+
* Per-project `tsconfig.spec.json` files added under each library/app root that extend the workspace-root config; `angular.json` `test.options.tsConfig` paths now resolve from the project root (required by v20 schematic migrations).
31+
* Schematics unit tests updated for the v20 `@schematics/angular:application` output (file names changed from `app.component.html``app.html`).
32+
33+
### BREAKING CHANGES — Deprecated API removals
34+
35+
Removed long-standing deprecated symbols. Use `ng update @koobiq/components@20` for assisted migration (schematics TBD — track follow-up).
36+
37+
#### Removed packages
38+
39+
* **`@koobiq/components/navbar-ic`** — entire package deleted. Migrate to `@koobiq/components/navbar` (`KbqNavbar`, `KbqNavbarItem`, `KbqNavbarModule`).
40+
* **`@koobiq/components/risk-level`** — entire package deleted. Migrate to `@koobiq/components/badge` (`KbqBadge` with `[outline]` and `[badgeColor]`). Note: Badge default density and color enum differ — verify visual parity.
41+
* **`@koobiq/components-experimental/form-field`** — sub-package deleted. Migrate to `@koobiq/components/form-field`. The experimental package was a transitional fork that has been merged back.
42+
43+
#### Removed core symbols
44+
45+
* `AnimationCurves` enum → use `KbqAnimationCurves`.
46+
* `MeasurementSystem` enum → use `KbqMeasurementSystem`.
47+
* `SizeUnitsConfig` interface → use `KbqSizeUnitsConfig`.
48+
* `KbqCommonModule`, `KBQ_SANITY_CHECKS`, `mcSanityChecksFactory` → no longer used.
49+
* `toBoolean()` → use Angular `booleanAttribute` from `@angular/core`.
50+
* `RdxAccordionItemState` → use `KbqAccordionItemState`.
51+
* `KbqCodeFile` → use `KbqCodeBlockFile`.
52+
* `KBQ_SIDEPANEL_WITH_SHADOW` token → removed.
53+
* `KbqSidepanelConfig.requiredBackdrop` field → removed (single shared backdrop used).
54+
* `formatDataSize()` → use `getFormattedSizeParts()`.
55+
* `getFormattedSizeParts(value, precision, system)` 3-arg overload → use 2-arg `getFormattedSizeParts(value, system)`.
56+
* `KBQ_VALIDATION` token and `KbqValidationOptions` interface → removed with legacy validation pipeline.
57+
* `kbqDisableLegacyValidationDirectiveProvider()` — the no-op shim kept after the `KbqValidateDirective` removal is also gone in v20.0.0. Run `ng update @koobiq/components@20` to auto-strip the call sites and import; the schematic also flags the resulting `providers: []` arrays for manual cleanup.
58+
59+
#### Removed component methods / inputs
60+
61+
* `KbqAutocompleteTrigger.openPanel()` → use `open()`.
62+
* `KbqClampedText.toggleIsCollapsed()` → use `toggle()`.
63+
* `KbqDivider.inset` input → removed.
64+
* `KbqTagList`: `multiple`, `compareWith`, `emitOnTagChanges`, `orientation`, `selectionModel`, `tagChanges`, `setSelectionByValue()` — all unused, removed.
65+
* `KbqTagInput`: `countOfSymbolsForUpdateWidth`, `updateInputWidth()` — unused, removed.
66+
* `KbqFormField.canShowStepper` → use `hasStepper` (stepper is always visible when provided).
67+
* `KbqAppSwitcherTrigger.apps` input → use `sites` with a single-element array.
68+
69+
#### Removed validation
70+
71+
* **`KbqValidateDirective`** — legacy validation directive deleted entirely. The new behaviour relies exclusively on `ErrorStateMatcher`. Consumers that relied on the legacy "show errors only after blur/submit" pattern should wire `ShowOnSubmitErrorStateMatcher` (or similar) explicitly via `errorStateMatcher` input or `ErrorStateMatcher` provider. The "lazy validation" behaviour (suppress required-not-shown until submit) is gone.
72+
73+
#### Removed file-upload validation API
74+
75+
* `KbqInputFile`, `KbqInputFileLabel` interfaces — removed.
76+
* `KbqFileValidatorFn` type → removed.
77+
* `isCorrectExtension()` function → use `FileValidators.isCorrectExtension` (`ValidatorFn`).
78+
* `KbqMultipleFileUploadComponent.errors`, `customValidation`, `hasErrors` → use `FormControl.errors` and `FormControl` validators.
79+
* `KbqSingleFileUploadComponent.errors`, `customValidation` → same.
80+
81+
#### Removed modal API
82+
83+
* `ModalOptions.kbqComponentParams` → use `data` field + `inject(KBQ_MODAL_DATA)` in the child component.
84+
* `KbqModalComponent.kbqComponentParams` @Input → removed.
85+
86+
#### Removed filter-bar API
87+
88+
* `KbqFilters.onSaveAsNew` Output → use `onSave` with `status === 'newFilter'`.
89+
* **`KbqFilterBarSearch`** component (`<kbq-filter-search>`) — removed. Use `<kbq-search-expandable [formControl]="searchControl" />` instead. Note: `kbq-search-expandable` requires a `FormControl`/`NgModel` binding.
90+
91+
#### Removed form-field directives
92+
93+
* **`KbqDatepickerToggle`** component (`<kbq-datepicker-toggle>`) — removed. Use `<kbq-datepicker-toggle-icon>` (`KbqDatepickerToggleIconComponent`) instead.
94+
* **`KbqFormFieldWithoutBorders`** directive (`<kbq-form-field kbqFormFieldWithoutBorders>`) — removed. Use the `noBorders` input on `KbqFormField`: `<kbq-form-field noBorders>`.
95+
96+
#### Removed tooltip modifier triggers
97+
98+
* **`KbqWarningTooltipTrigger`** (`[kbqWarningTooltip]`) and **`KbqExtendedTooltipTrigger`** (`[kbqExtendedTooltip]`) directives removed. Use the base `[kbqTooltip]` directive with the new public `kbqTooltipModifier` input:
99+
100+
```html
101+
<!-- before -->
102+
<kbq-form-field #tooltip="kbqWarningTooltip" [kbqWarningTooltip]="msg">
103+
104+
<!-- after -->
105+
<kbq-form-field #tooltip="kbqTooltip" kbqTooltipModifier="warning" [kbqTooltip]="msg">
106+
```
107+
108+
For the extended variant, `[kbqTooltipHeader]` is now also exposed on the base trigger:
109+
110+
```html
111+
<!-- before -->
112+
<button [kbqExtendedTooltip]="content" [kbqTooltipHeader]="header">
113+
114+
<!-- after -->
115+
<button kbqTooltipModifier="extended" [kbqTooltip]="content" [kbqTooltipHeader]="header">
116+
```
117+
118+
`KbqDatepickerInput.kbqValidationTooltip` and `KbqTimepicker.kbqValidationTooltip` setters now accept `KbqTooltipTrigger` (the base class) instead of `KbqWarningTooltipTrigger`.
119+
120+
### Migration
121+
122+
Consumers can run the automatic migration:
123+
124+
```
125+
ng update @koobiq/components@20
126+
```
127+
128+
This invokes the `v20-upgrade` schematic which rewrites your codebase in place:
129+
130+
- Imports from `@koobiq/components/navbar-ic` / `risk-level` / `components-experimental/form-field` are remapped to the surviving packages (`navbar`, `badge`, `components/form-field`).
131+
- Identifier renames in `.ts` files (`KbqNavbarIc*``Kbq*`, `KbqRiskLevel*``KbqBadge*`, `KbqWarningTooltipTrigger` / `KbqExtendedTooltipTrigger``KbqTooltipTrigger`, `KbqDatepickerToggle``KbqDatepickerToggleIconComponent`, `KbqFilterBarSearch``KbqSearchExpandable`, `RdxAccordionItemState``KbqAccordionItemState`, `KbqCodeFile``KbqCodeBlockFile`, `AnimationCurves``KbqAnimationCurves`, `MeasurementSystem``KbqMeasurementSystem`, `SizeUnitsConfig``KbqSizeUnitsConfig`, `KbqFormFieldRef``KbqFormField`).
132+
- Token / function renames (`toBoolean(``booleanAttribute(`, `isCorrectExtension(``FileValidators.isCorrectExtension(`, `formatDataSize(``getFormattedSizeParts(`, `kbqComponentParams:``data:`); dropped tokens `KBQ_VALIDATION`, `KBQ_SANITY_CHECKS`, `KBQ_SIDEPANEL_WITH_SHADOW` removed from imports.
133+
- Instance method renames (`.openPanel(``.open(`, `.toggleIsCollapsed(``.toggle(`, `.focusViaKeyboard(``.focus(`).
134+
- Template selectors (`<kbq-filter-search>``<kbq-search-expandable>`, `<kbq-datepicker-toggle>``<kbq-datepicker-toggle-icon>`, `<kbq-risk-level>``<kbq-badge>`, `<kbq-navbar-ic*>``<kbq-navbar*>`).
135+
- Template attributes (`kbqFormFieldWithoutBorders``noBorders`, `[kbqWarningTooltip]``kbqTooltipModifier="warning" [kbqTooltip]`, `[kbqExtendedTooltip]``kbqTooltipModifier="extended" [kbqTooltip]`, template-ref `="kbqWarningTooltip"``="kbqTooltip"`).
136+
- SCSS class selectors (`.kbq-risk-level``.kbq-badge`, `.kbq-navbar-ic``.kbq-navbar`, etc.).
137+
138+
The schematic prints warnings for structural changes it cannot safely auto-fix:
139+
140+
- `(onSaveAsNew)` listeners on `<kbq-filters>` — switch to `(onSave)` and branch on `$event.status === 'newFilter'`.
141+
- `[customValidation]` / `[errors]` on file-upload components — use `FormControl` validators / read `FormControl.errors`.
142+
- `[apps]` on `<button kbqAppSwitcher>` — wrap into a single-site `[sites]="[{ id, name, apps }]"`.
143+
144+
After the schematic runs, **review the diff before committing**: the migration is regex-based and will not rewrite values held in local variables, re-exports, or aliased imports.
145+
146+
### Known follow-ups
147+
148+
* Refactor remaining `(instance as any).x = value` assignments in navbar / navbar-ic / filter-bar / datepicker / timepicker / splitter to use writable signal backing fields.
149+
* Refactor `KbqAppSwitcherComponent.trigger` and `KbqAppSwitcherComponent.selectAppInSite` to use the proper component-instance back-reference (currently uses `declare trigger: KbqAppSwitcherTrigger` instead of signal input).
150+
* Update Jest tests that mutate signal inputs directly (~306 select/option/model-input-related tests need `instance.foo.set(value)` instead of `instance.foo = value`).
151+
* Run signal-input / signal-queries / output migrations from `@angular/core:ng-generate/*` on the remaining `@Input` getter/setter pairs that survived this PR.
152+
1153
# 19.8.0 (2026-05-26)
2154

3155
### Koobiq

0 commit comments

Comments
 (0)