Skip to content

Upgrade Angular 20 → 21 (LTS) stepped upgrade - #734

Merged
fpigeonjr merged 3 commits into
masterfrom
gh-575-angular-20-21-lts-stepped-upgrade
Sep 11, 2026
Merged

Upgrade Angular 20 → 21 (LTS) stepped upgrade#734
fpigeonjr merged 3 commits into
masterfrom
gh-575-angular-20-21-lts-stepped-upgrade

Conversation

@fpigeonjr

Copy link
Copy Markdown
Contributor

Description

Steps the library from Angular 20 to Angular 21 (LTS) via the supported ng update path, bumping the test-app harness to Angular 21 in lockstep, per ADR-0001's epic target.

  • Root library (package.json): peer deps bumped to @angular/*@^21.2.23, @angular/cli@^21.2.24, zone.js@^0.16.0, angular-eslint@21.4.0, typescript@5.9.3. @fortawesome/angular-fontawesome moved ~3.0.0~4.0.0 (the only published version with an Angular 21 peer). Package version bumped to 21.0.0.
  • test-app harness: @angular/*@21.2.23, @angular/cli/build-angular@21.2.24, @angular/cdk@21.2.14, @angular-builders/custom-esbuild@21.1.0, @analogjs/vite-plugin-angular/vitest-angular@2.7.2, zone.js@0.16.3, typescript@5.9.3, and an explicit vite@6.4.2 pin (previously only enforced via overrides).
  • ng update migrations applied: test-app/src/main.ts bootstrap now passes applicationProviders: [provideZoneChangeDetection()]; test-app/tsconfig.json dropped the redundant lib override.
  • Source fixes for removed/changed CDK & core APIs:
    • tab-body.ts / tab-label.ts / their templates: PortalHostDirective, TemplatePortalDirective, and the cdkPortalHost selector (all removed in CDK 21) replaced with CdkPortalOutlet / CdkPortal / cdkPortalOutlet.
    • data-table.component.ts: dropped the _VIEW_REPEATER_STRATEGY / _DisposeViewRepeaterStrategy provider, which is no longer exported from @angular/cdk/collectionsCdkTable now selects its own repeater strategy internally.
    • fieldset-wrapper.component.ts / label-wrapper.component.ts: @HostListener("window:resize", ["$event"]) handlers now accept the $event parameter (Angular 21 no longer tolerates an argument-count mismatch here).
    • test-app/src/test-setup.ts: Angular 21 changed TestBed's default change-detection strategy to zoneless, which broke the majority of existing specs with ExpressionChangedAfterItHasBeenCheckedError on any second detectChanges() call following a bound-property mutation (previously legal, zone-based behavior). Restored zone-based change detection globally via a beforeEach that provides provideZoneChangeDetection(), preserving pre-21 spec semantics without touching every spec file individually.

Motivation and Context

Parent epic: #562 (Jira: IAEMOD-61187). Unblocked by #574 (Angular 19 → 20 upgrade), which is closed.

Closes #575

Type of Change (Select One and Apply Label)

  • Bug fix (non-breaking change which fixes an issue) → Apply bugfix label
  • New feature (non-breaking change which adds functionality) → Apply enhancement label
  • Breaking change (fix or feature that would cause existing functionality to change) → Apply breaking label
  • Documentation / configuration update → Apply maintenance label

How to Test

  1. npm ci && npm ci --prefix test-app
  2. npm --prefix test-app test — confirm all 155 spec files / 1424 tests pass
  3. npm run coverage:check — confirm coverage stays at/above the ratcheted floor
  4. npm --prefix test-app run build — confirm the harness still builds (pre-existing Sass @import deprecation warnings are expected and unrelated)
  5. npm run lint && npm --prefix test-app run lint — confirm no new lint errors and warnings stay within baseline
  6. npm run format:check — confirm formatting is clean
  7. npm run validate:publish — confirms npm pack --dry-run succeeds and all frozen deep-import paths (scripts/consumer-deep-imports.json) still resolve

Expected result: All commands above exit 0; the test suite reports 155 passed (155) files / 1424 passed (1424) tests.

Screenshots (if appropriate)

N/A — dependency upgrade and internal API-compat fixes only, no visual changes.

Checklist

  • Branch name follows convention (e.g. gh-<number>-<slug>)
  • PR title starts with a verb in the imperative mood
  • I have self-reviewed my own code
  • format:check passes (npm run format:check)
  • lint passes (npm run lint)
  • build passes (cd test-app && npm run build)
  • Tests pass and coverage is reported (cd test-app && npm test)
  • If this change requires a documentation update, I have updated it accordingly
  • If there are dependent changes, they have been merged and published in downstream modules

Steps the library and test-app harness from Angular 20 to Angular 21
via the supported ng update path, per ADR-0001's epic target.

- Bump root peer deps to @angular/*@^21.2.23, @angular/cli@^21.2.24,
  zone.js@^0.16.0, angular-eslint@21.4.0, typescript@5.9.3; move
  @fortawesome/angular-fontawesome ~3.0.0 -> ~4.0.0 (only version with
  an Angular 21 peer). Package version -> 21.0.0.
- Bump test-app harness to @angular/*@21.2.23, @angular/cli@21.2.24,
  @angular/cdk@21.2.14, @angular-builders/custom-esbuild@21.1.0,
  @analogjs/vite-plugin-angular/vitest-angular@2.7.2, zone.js@0.16.3,
  typescript@5.9.3; pin vite@6.4.2 explicitly.
- Apply ng update migrations: main.ts bootstrap now passes
  applicationProviders: [provideZoneChangeDetection()]; tsconfig.json
  drops the redundant lib override.
- Fix removed/changed CDK & core APIs: tab-body/tab-label move off
  PortalHostDirective/TemplatePortalDirective/cdkPortalHost (removed
  in CDK 21) onto CdkPortalOutlet/CdkPortal/cdkPortalOutlet;
  data-table drops the _VIEW_REPEATER_STRATEGY provider (no longer
  exported by @angular/cdk/collections); fieldset/label-wrapper
  window:resize handlers now accept the $event parameter.
- Restore zone-based change detection globally in test-setup.ts via
  provideZoneChangeDetection(), since Angular 21 defaults TestBed to
  zoneless CD, which broke the majority of existing specs with
  ExpressionChangedAfterItHasBeenCheckedError on a second
  detectChanges() call after a bound-property mutation.

Closes #575
@fpigeonjr fpigeonjr added angular-upgrade Angular version upgrade epic (IAEMOD-57412) maintenance Repo maintenance / tooling labels Sep 11, 2026
@fpigeonjr fpigeonjr self-assigned this Sep 11, 2026
@fpigeonjr
fpigeonjr requested a balanced review from Copilot September 11, 2026 20:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The resize fixes introduce unnecessary public signature changes, and the migrated custom-label portal path lacks focused coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Upgrades the raw-source library and test harness from Angular 20 to Angular 21 while adapting removed CDK APIs and preserving zone-based behavior.

Changes:

  • Updates Angular, CDK, TypeScript, Zone.js, and supporting tooling.
  • Migrates portal and table integrations for CDK 21.
  • Restores zone-based test and application bootstrapping behavior.
File summaries
File Description
package.json Updates library version and peer/tooling dependencies.
test-app/package.json Upgrades the Angular test harness dependencies.
test-app/tsconfig.json Removes the redundant library override.
test-app/src/main.ts Enables zone-based change detection at bootstrap.
test-app/src/test-setup.ts Restores zone-based TestBed behavior globally.
src/ui-kit/components/data-table/data-table.component.ts Removes the obsolete view-repeater provider.
src/ui-kit/experimental/tabs/tab-body.ts Migrates to CdkPortalOutlet.
src/ui-kit/experimental/tabs/tab-body.html Updates the portal outlet selector.
src/ui-kit/experimental/tabs/tab-label.ts Migrates to CdkPortal.
src/ui-kit/experimental/tabs/tab-group.html Updates custom-label portal rendering.
src/ui-kit/wrappers/fieldset-wrapper/fieldset-wrapper.component.ts Adjusts resize-listener handling.
src/ui-kit/wrappers/label-wrapper/label-wrapper.component.ts Adjusts resize-listener handling.
Review details
  • Files reviewed: 12/14 changed files
  • Comments generated: 3
  • Review effort level: Balanced

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

Comment thread src/ui-kit/wrappers/fieldset-wrapper/fieldset-wrapper.component.ts Outdated
Comment thread src/ui-kit/wrappers/label-wrapper/label-wrapper.component.ts Outdated
Comment thread src/ui-kit/experimental/tabs/tab-group.html
- fieldset-wrapper/label-wrapper: revert onResize() to a zero-argument
  @HostListener('window:resize') instead of adding a required $event
  parameter, matching the existing page.component.ts precedent and
  avoiding an unnecessary public signature change for deep-import
  consumers.
- tab-group.spec.ts: add a focused test rendering an
  <ng-template sam-tab-label> to exercise the CdkPortalOutlet /
  MdTabLabel migration path, which was previously untested.
@fpigeonjr
fpigeonjr marked this pull request as ready for review September 11, 2026 21:10
@fpigeonjr
fpigeonjr requested a review from a team as a code owner September 11, 2026 21:10
The library runs on Vitest, not Jasmine/Karma, and no source file
references Jasmine globals. Drops the stale @types/jasmine
devDependency and the 'jasmine' entry from tsconfig.json's
compilerOptions.types.
@fpigeonjr
fpigeonjr merged commit c307c6f into master Sep 11, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

angular-upgrade Angular version upgrade epic (IAEMOD-57412) maintenance Repo maintenance / tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Angular 20 → 21 (LTS) stepped upgrade

3 participants