1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-15847] libs/components strict migration (#15738)

This PR migrates `libs/components` to use strict TypeScript.

- Remove `@ts-strict-ignore` from each file in `libs/components` and resolved any new compilation errors
- Converted ViewChild and ContentChild decorators to use the new signal-based queries using the [Angular signal queries migration](https://angular.dev/reference/migrations/signal-queries)
  - Made view/content children `required` where appropriate, eliminating the need for additional null checking. This helped simplify the strict migration.

---

Co-authored-by: Vicki League <vleague@bitwarden.com>
This commit is contained in:
Will Martin
2025-08-18 15:36:45 -04:00
committed by GitHub
parent f2d2d0a767
commit 827c4c0301
77 changed files with 450 additions and 612 deletions

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, booleanAttribute, input } from "@angular/core";
import { MappedOptionComponent } from "./option";

View File

@@ -1,6 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { hasModifierKey } from "@angular/cdk/keycodes";
import {
Component,
@@ -10,7 +7,6 @@ import {
Optional,
QueryList,
Self,
ViewChild,
Output,
EventEmitter,
input,
@@ -18,6 +14,7 @@ import {
computed,
model,
signal,
viewChild,
} from "@angular/core";
import {
ControlValueAccessor,
@@ -47,7 +44,7 @@ let nextId = 0;
},
})
export class SelectComponent<T> implements BitFormFieldControl, ControlValueAccessor {
@ViewChild(NgSelectComponent) select: NgSelectComponent;
readonly select = viewChild.required(NgSelectComponent);
/** Optional: Options can be provided using an array input or using `bit-option` */
readonly items = model<Option<T>[] | undefined>();
@@ -55,13 +52,13 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
readonly placeholder = input(this.i18nService.t("selectPlaceholder"));
@Output() closed = new EventEmitter();
protected selectedValue = signal<T>(undefined);
selectedOption: Signal<Option<T>> = computed(() =>
protected selectedValue = signal<T | undefined | null>(undefined);
selectedOption: Signal<Option<T> | null | undefined> = computed(() =>
this.findSelectedOption(this.items(), this.selectedValue()),
);
protected searchInputId = `bit-select-search-input-${nextId++}`;
private notifyOnChange?: (value: T) => void;
private notifyOnChange?: (value?: T | null) => void;
private notifyOnTouched?: () => void;
constructor(
@@ -104,7 +101,7 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
set disabled(value: any) {
this._disabled = value != null && value !== false;
}
private _disabled: boolean;
private _disabled?: boolean;
/**Implemented as part of NG_VALUE_ACCESSOR */
writeValue(obj: T): void {
@@ -112,7 +109,7 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
}
/**Implemented as part of NG_VALUE_ACCESSOR */
registerOnChange(fn: (value: T) => void): void {
registerOnChange(fn: (value?: T | null) => void): void {
this.notifyOnChange = fn;
}
@@ -151,11 +148,11 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
get ariaDescribedBy() {
return this._ariaDescribedBy;
}
set ariaDescribedBy(value: string) {
set ariaDescribedBy(value: string | undefined) {
this._ariaDescribedBy = value;
this.select?.searchInput.nativeElement.setAttribute("aria-describedby", value);
this.select()?.searchInput.nativeElement.setAttribute("aria-describedby", value ?? "");
}
private _ariaDescribedBy: string;
private _ariaDescribedBy?: string;
/**Implemented as part of BitFormFieldControl */
get labelForId() {
@@ -176,20 +173,24 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
set required(value: any) {
this._required = value != null && value !== false;
}
private _required: boolean;
private _required?: boolean;
/**Implemented as part of BitFormFieldControl */
get hasError() {
return this.ngControl?.status === "INVALID" && this.ngControl?.touched;
return !!(this.ngControl?.status === "INVALID" && this.ngControl?.touched);
}
/**Implemented as part of BitFormFieldControl */
get error(): [string, any] {
const key = Object.keys(this.ngControl?.errors)[0];
return [key, this.ngControl?.errors[key]];
const errors = this.ngControl?.errors ?? {};
const key = Object.keys(errors)[0];
return [key, errors[key]];
}
private findSelectedOption(items: Option<T>[] | undefined, value: T): Option<T> | undefined {
private findSelectedOption(
items: Option<T>[] | undefined,
value: T | null | undefined,
): Option<T> | undefined {
return items?.find((item) => item.value === value);
}
@@ -207,7 +208,7 @@ export class SelectComponent<T> implements BitFormFieldControl, ControlValueAcce
* Needs to be arrow function to retain `this` scope.
*/
protected onKeyDown = (event: KeyboardEvent) => {
if (this.select.isOpen && event.key === "Escape" && !hasModifierKey(event)) {
if (this.select().isOpen && event.key === "Escape" && !hasModifierKey(event)) {
event.stopPropagation();
}