|
| 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 | + |
1 | 153 | # 19.8.0 (2026-05-26) |
2 | 154 |
|
3 | 155 | ### Koobiq |
|
0 commit comments