mirror of
https://github.com/bitwarden/clients.git
synced 2026-06-04 21:04:29 +08:00
[CL-954] Upgrade to Angular 21 (#19725)
* prune desktop packages
* Fix @napi-rs/cli version mismatch in desktop napi workspace
Aligns package.json declaration with the locked version (3.2.0) to
resolve npm workspace inconsistency that was blocking ng update.
* update Storybook to v10
* update Angular to v21
* override jest in ng builder
* Add jest-environment-jsdom as explicit dependency
Previously installed as a side effect of a jest@29 override; removing
that override caused it to disappear from node_modules.
* Add .claude/worktrees/ to .gitignore
* Restore @napi-rs/cli to 3.5.1 to match main
* Pin jest-environment-jsdom to 29.7.0 and add to renovate config
* Override jest-environment-jsdom to 29.7.0 in build-angular context
* Add isolatedModules to libs/subscription tsconfig.spec.json to fix Angular 21 module resolution
* Change moduleResolution to bundler for Angular 21 subpath export compatibility
* Add isolatedModules to Angular libs with old spec tsconfig pattern
* Disable emitDecoratorMetadata in spec tsconfigs with isolatedModules
* Fix HostListener event parameter types for Angular 21 compiler strictness
* Revert accidental change to access-selector spec
* Remove accidentally generated desktop package-lock.json
* Fix type-only imports/exports caught by Rolldown in Storybook v10/Vite v8
* fix vault-wrapper type error from Angular 21 stricter generic inference
ngComponentOutlet accepts Type<unknown>; annotate computed() explicitly
since VaultComponent is generic and VaultOrigComponent is not, preventing
TypeScript from inferring a compatible union constructor type.
* Fix kitchen-sink interaction tests for Storybook v10
Replace fire-and-forget navigateTo + synchronous getByRole with
navigateAndWaitFor<T>, which sets the hash and retries the ready
callback via waitFor. Storybook v10 starts play functions before
Angular's initial router navigation completes, so synchronous DOM
queries after navigation were failing intermittently.
* Provide ZoneJS change detection scheduler for Storybook stories
Angular 21 no longer sets up the ZoneJS change detection scheduler by
default in bootstrapApplication. Storybook's Angular renderer uses
bootstrapApplication internally and does not add provideZoneChangeDetection
automatically, so Default CD components relying on zone.js to trigger
re-renders after async operations were not updating before Chromatic
snapshots.
* Wait for dialog/side nav to render before Chromatic snapshot
After userEvent.click the dialog and side nav open asynchronously.
Without an explicit waitFor, Chromatic captures the snapshot before the
resulting UI state is present.
* Fix kitchen-sink waitFor: re-query side nav button, use querySelector for dialog
- openSideNav: re-query the toggle button inside waitFor to avoid reading
a stale DOM reference after Angular re-renders the element post-click
- SimpleDialogOpen / VirtualScrollBlockingDialog: replace getByRole("dialog")
with querySelector("cdk-dialog-container") to avoid testing-library's
visibility check failing on a momentarily inaccessible overlay element
* Revert kitchen-sink stories to main
* Bump Angular, Storybook, and ng-select to latest patch versions
* Trigger pre-commit hooks on merge
* Regenerate package-lock.json with --force to fix npm ci sync
This commit is contained in:
parent
7caeab7de5
commit
69c937e592
2
.github/renovate.json5
vendored
2
.github/renovate.json5
vendored
@ -183,6 +183,7 @@
|
||||
"itertools",
|
||||
"jest",
|
||||
"jest-diff",
|
||||
"jest-environment-jsdom",
|
||||
"jest-junit",
|
||||
"jest-mock-extended",
|
||||
"jest-preset-angular",
|
||||
@ -515,6 +516,7 @@
|
||||
"interprocess",
|
||||
"jest",
|
||||
"jest-diff",
|
||||
"jest-environment-jsdom",
|
||||
"jest-junit",
|
||||
"jest-mock-extended",
|
||||
"jest-preset-angular",
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
# General
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.claude/worktrees/
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
// This file has been automatically migrated to valid ESM format by Storybook.
|
||||
import { createRequire } from "node:module";
|
||||
import { dirname, join } from "path";
|
||||
|
||||
import { StorybookConfig } from "@storybook/angular";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
"../libs/auth/src/**/*.mdx",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { provideZoneChangeDetection } from "@angular/core";
|
||||
import { setCompodocJson } from "@storybook/addon-docs/angular";
|
||||
import { withThemeByClassName } from "@storybook/addon-themes";
|
||||
import { componentWrapperDecorator } from "@storybook/angular";
|
||||
import { applicationConfig, componentWrapperDecorator } from "@storybook/angular";
|
||||
import type { Preview } from "@storybook/angular";
|
||||
|
||||
import docJson from "../documentation.json";
|
||||
@ -17,6 +18,9 @@ const wrapperDecorator = componentWrapperDecorator((story) => {
|
||||
|
||||
const preview: Preview = {
|
||||
decorators: [
|
||||
applicationConfig({
|
||||
providers: [provideZoneChangeDetection()],
|
||||
}),
|
||||
withThemeByClassName({
|
||||
themes: {
|
||||
light: "theme_light",
|
||||
|
||||
@ -2,11 +2,12 @@ import createEmotion from "@emotion/css/create-instance";
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { property, state } from "lit/decorators.js";
|
||||
|
||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
import type { Theme } from "@bitwarden/common/platform/enums";
|
||||
import { ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { EventSecurity } from "../../../utils/event-security";
|
||||
import { OptionSelectionButton } from "../buttons/option-selection-button";
|
||||
import { Option } from "../common-types";
|
||||
import type { Option } from "../common-types";
|
||||
|
||||
import { optionItemIconWidth } from "./option-item";
|
||||
import { OptionItems, optionsMenuItemMaxWidth } from "./option-items";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { enableProdMode, provideZoneChangeDetection } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
import { PopupSizeService } from "../platform/popup/layout/popup-size.service";
|
||||
@ -20,7 +20,9 @@ if (process.env.ENV === "production") {
|
||||
}
|
||||
|
||||
function init() {
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule, {
|
||||
applicationProviders: [provideZoneChangeDetection()],
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import "core-js/proposals/explicit-resource-management";
|
||||
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { enableProdMode, provideZoneChangeDetection } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
@ -14,7 +14,9 @@ if (!ipc.platform.isDev) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule, {
|
||||
applicationProviders: [provideZoneChangeDetection()],
|
||||
});
|
||||
|
||||
// Disable drag and drop to prevent malicious links from executing in the context of the app
|
||||
document.addEventListener("dragover", (event) => event.preventDefault());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { Component, computed, inject } from "@angular/core";
|
||||
import { Component, computed, inject, Type } from "@angular/core";
|
||||
import { toSignal } from "@angular/core/rxjs-interop";
|
||||
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
@ -22,7 +22,7 @@ export class VaultWrapperComponent {
|
||||
this.configService.getFeatureFlag$(FeatureFlag.DesktopUiMigrationMilestone3),
|
||||
);
|
||||
|
||||
protected readonly componentToRender = computed(() =>
|
||||
protected readonly componentToRender = computed<Type<unknown>>(() =>
|
||||
this.useMilestone3() ? VaultComponent : VaultOrigComponent,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { enableProdMode, provideZoneChangeDetection } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
import { AppModule } from "./app/app.module";
|
||||
@ -7,4 +7,6 @@ if (process.env.NODE_ENV === "production") {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule, {
|
||||
applicationProviders: [provideZoneChangeDetection()],
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { enableProdMode } from "@angular/core";
|
||||
import { enableProdMode, provideZoneChangeDetection } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
import { AppModule } from "./app/app.module";
|
||||
@ -7,4 +7,6 @@ if (process.env.NODE_ENV === "production") {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
void platformBrowserDynamic().bootstrapModule(AppModule, {
|
||||
applicationProviders: [provideZoneChangeDetection()],
|
||||
});
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
"types": ["jest", "node"],
|
||||
"isolatedModules": true,
|
||||
"emitDecoratorMetadata": false
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
|
||||
@ -96,8 +96,10 @@ export class BitFormFieldComponent implements AfterContentChecked {
|
||||
*/
|
||||
protected readonly defaultContentIsFocused = signal(false);
|
||||
@HostListener("focusin", ["$event.target"])
|
||||
onFocusIn(target: HTMLElement) {
|
||||
this.defaultContentIsFocused.set(target.matches("[data-default-content] *:focus-visible"));
|
||||
onFocusIn(target: EventTarget) {
|
||||
this.defaultContentIsFocused.set(
|
||||
(target as HTMLElement).matches("[data-default-content] *:focus-visible"),
|
||||
);
|
||||
}
|
||||
@HostListener("focusout")
|
||||
onFocusOut() {
|
||||
|
||||
@ -24,8 +24,8 @@ export class ItemComponent {
|
||||
*/
|
||||
protected readonly focusVisibleWithin = signal(false);
|
||||
@HostListener("focusin", ["$event.target"])
|
||||
onFocusIn(target: HTMLElement) {
|
||||
this.focusVisibleWithin.set(target.matches("[data-fvw-target]:focus-visible"));
|
||||
onFocusIn(target: EventTarget) {
|
||||
this.focusVisibleWithin.set((target as HTMLElement).matches("[data-fvw-target]:focus-visible"));
|
||||
}
|
||||
@HostListener("focusout")
|
||||
onFocusOut() {
|
||||
|
||||
@ -282,7 +282,7 @@ export class LayoutComponent {
|
||||
* @see https://github.com/angular/components/issues/10247#issuecomment-384060265
|
||||
**/
|
||||
private readonly skipLink = viewChild.required<LinkComponent>("skipLink");
|
||||
handleKeydown(ev: KeyboardEvent) {
|
||||
handleKeydown(ev: Event) {
|
||||
if (isNothingFocused()) {
|
||||
ev.preventDefault();
|
||||
this.skipLink().el.nativeElement.focus();
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { provideZoneChangeDetection } from "@angular/core";
|
||||
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
|
||||
|
||||
import { AppModule } from "./app/app.module";
|
||||
|
||||
platformBrowserDynamic()
|
||||
.bootstrapModule(AppModule)
|
||||
.bootstrapModule(AppModule, { applicationProviders: [provideZoneChangeDetection()] })
|
||||
.catch((err) => console.error(err)); // eslint-disable-line
|
||||
|
||||
@ -121,8 +121,8 @@ export class NavItemComponent extends NavBaseComponent {
|
||||
: "",
|
||||
);
|
||||
|
||||
protected onFocusIn(target: HTMLElement) {
|
||||
this.focusVisibleWithin.set(target.matches("[data-fvw]:focus-visible"));
|
||||
protected onFocusIn(target: EventTarget) {
|
||||
this.focusVisibleWithin.set((target as HTMLElement).matches("[data-fvw]:focus-visible"));
|
||||
}
|
||||
|
||||
protected onFocusOut() {
|
||||
|
||||
@ -84,8 +84,8 @@ export class TabLinkComponent implements FocusableOption, AfterViewInit {
|
||||
/** Roving tabindex value — parent nav-bar sets one link to 0 and the rest to -1. */
|
||||
readonly tabIndex = signal(-1);
|
||||
|
||||
@HostListener("keydown", ["$event"]) onKeyDown(event: KeyboardEvent) {
|
||||
if (event.code === "Space") {
|
||||
@HostListener("keydown", ["$event"]) onKeyDown(event: Event) {
|
||||
if ((event as KeyboardEvent).code === "Space") {
|
||||
this.tabItem().click();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
"types": ["jest", "node"],
|
||||
"isolatedModules": true,
|
||||
"emitDecoratorMetadata": false
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
|
||||
@ -3,4 +3,4 @@ export { LogLevel } from "./log-level";
|
||||
export { ConsoleLogService } from "./console-log.service";
|
||||
export { FlightRecorder } from "./flight-recorder";
|
||||
export { buildFlightRecorderCsvExport } from "./flight-recorder-export";
|
||||
export { FlightRecorderEvent } from "@bitwarden/sdk-internal";
|
||||
export type { FlightRecorderEvent } from "@bitwarden/sdk-internal";
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
"types": ["jest", "node"],
|
||||
"isolatedModules": true,
|
||||
"emitDecoratorMetadata": false
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"types": ["jest", "node"]
|
||||
"types": ["jest", "node"],
|
||||
"isolatedModules": true,
|
||||
"emitDecoratorMetadata": false
|
||||
},
|
||||
"include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"]
|
||||
}
|
||||
|
||||
21341
package-lock.json
generated
21341
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
54
package.json
54
package.json
@ -46,10 +46,10 @@
|
||||
"libs/**/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "20.3.12",
|
||||
"@angular-eslint/schematics": "20.7.0",
|
||||
"@angular/cli": "20.3.12",
|
||||
"@angular/compiler-cli": "20.3.18",
|
||||
"@angular-devkit/build-angular": "21.2.9",
|
||||
"@angular-eslint/schematics": "21.3.1",
|
||||
"@angular/cli": "21.2.9",
|
||||
"@angular/compiler-cli": "21.2.11",
|
||||
"@babel/core": "7.29.0",
|
||||
"@babel/preset-env": "7.29.2",
|
||||
"@compodoc/compodoc": "1.2.1",
|
||||
@ -57,20 +57,20 @@
|
||||
"@electron/rebuild": "4.0.3",
|
||||
"@eslint/compat": "2.0.5",
|
||||
"@lit-labs/signals": "0.2.0",
|
||||
"@ngtools/webpack": "20.3.12",
|
||||
"@ngtools/webpack": "21.2.9",
|
||||
"@storybook/addon-a11y": "10.3.6",
|
||||
"@storybook/addon-designs": "11.1.3",
|
||||
"@storybook/addon-docs": "10.3.6",
|
||||
"@storybook/addon-links": "10.3.6",
|
||||
"@storybook/addon-themes": "10.3.6",
|
||||
"@storybook/angular": "10.3.6",
|
||||
"@storybook/test-runner": "0.24.3",
|
||||
"@storybook/web-components-vite": "10.3.6",
|
||||
"@nx/devkit": "22.6.5",
|
||||
"@nx/eslint": "22.6.5",
|
||||
"@nx/jest": "22.6.5",
|
||||
"@nx/js": "22.6.5",
|
||||
"@nx/webpack": "22.6.5",
|
||||
"@storybook/addon-a11y": "9.1.16",
|
||||
"@storybook/addon-designs": "9.0.0-next.3",
|
||||
"@storybook/addon-docs": "9.1.16",
|
||||
"@storybook/addon-links": "9.1.16",
|
||||
"@storybook/addon-themes": "9.1.16",
|
||||
"@storybook/angular": "9.1.16",
|
||||
"@storybook/test-runner": "0.22.0",
|
||||
"@storybook/web-components-vite": "9.1.16",
|
||||
"@tailwindcss/container-queries": "0.1.1",
|
||||
"@types/chrome": "0.1.28",
|
||||
"@types/firefox-webext-browser": "143.0.0",
|
||||
@ -118,7 +118,7 @@
|
||||
"eslint-plugin-jest": "29.15.2",
|
||||
"eslint-plugin-rxjs": "5.0.3",
|
||||
"eslint-plugin-rxjs-angular": "2.0.1",
|
||||
"eslint-plugin-storybook": "9.1.20",
|
||||
"eslint-plugin-storybook": "10.3.6",
|
||||
"eslint-plugin-tailwindcss": "3.18.3",
|
||||
"fantasticon": "4.1.0",
|
||||
"html-loader": "5.1.0",
|
||||
@ -127,6 +127,7 @@
|
||||
"husky": "9.1.7",
|
||||
"jest": "30.3.0",
|
||||
"jest-diff": "30.3.0",
|
||||
"jest-environment-jsdom": "29.7.0",
|
||||
"jest-junit": "17.0.0",
|
||||
"jest-mock-extended": "4.0.1",
|
||||
"jest-preset-angular": "16.1.4",
|
||||
@ -142,9 +143,9 @@
|
||||
"process": "0.11.10",
|
||||
"remark-gfm": "4.0.1",
|
||||
"rimraf": "6.1.2",
|
||||
"storybook": "10.3.6",
|
||||
"sass": "1.99.0",
|
||||
"sass-loader": "16.0.7",
|
||||
"storybook": "9.1.20",
|
||||
"style-loader": "4.0.0",
|
||||
"svgo": "4.0.1",
|
||||
"tailwindcss": "3.4.18",
|
||||
@ -164,15 +165,15 @@
|
||||
"webpack-node-externals": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "20.3.18",
|
||||
"@angular/cdk": "20.2.14",
|
||||
"@angular/common": "20.3.18",
|
||||
"@angular/compiler": "20.3.18",
|
||||
"@angular/core": "20.3.18",
|
||||
"@angular/forms": "20.3.18",
|
||||
"@angular/platform-browser": "20.3.18",
|
||||
"@angular/platform-browser-dynamic": "20.3.18",
|
||||
"@angular/router": "20.3.18",
|
||||
"@angular/animations": "21.2.11",
|
||||
"@angular/cdk": "21.2.9",
|
||||
"@angular/common": "21.2.11",
|
||||
"@angular/compiler": "21.2.11",
|
||||
"@angular/core": "21.2.11",
|
||||
"@angular/forms": "21.2.11",
|
||||
"@angular/platform-browser": "21.2.11",
|
||||
"@angular/platform-browser-dynamic": "21.2.11",
|
||||
"@angular/router": "21.2.11",
|
||||
"@bitwarden/commercial-sdk-internal": "0.2.0-main.757",
|
||||
"@bitwarden/desktop-napi": "file:apps/desktop/desktop_native/napi",
|
||||
"@bitwarden/sdk-internal": "0.2.0-main.757",
|
||||
@ -182,7 +183,7 @@
|
||||
"@koa/router": "15.4.0",
|
||||
"@microsoft/signalr": "10.0.0",
|
||||
"@microsoft/signalr-protocol-msgpack": "10.0.0",
|
||||
"@ng-select/ng-select": "20.7.0",
|
||||
"@ng-select/ng-select": "21.8.2",
|
||||
"big-integer": "1.6.52",
|
||||
"braintree-web-drop-in": "1.46.0",
|
||||
"buffer": "6.0.3",
|
||||
@ -234,6 +235,9 @@
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"@types/react": "18.3.27",
|
||||
"@angular-devkit/build-angular": {
|
||||
"jest-environment-jsdom": "29.7.0"
|
||||
},
|
||||
"tmp": ">=0.2.4"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"strict": false,
|
||||
"pretty": true,
|
||||
"moduleResolution": "node",
|
||||
"moduleResolution": "bundler",
|
||||
"noImplicitAny": true,
|
||||
"target": "ES2016",
|
||||
"module": "ES2020",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user