Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,14 @@ const baseTsLintConfig = {
{ "selector": "objectLiteralProperty", "format": null }
],
'@typescript-eslint/no-deprecated': 'error',
'@typescript-eslint/no-empty-function': 'warn',
'@typescript-eslint/no-empty-function': ['warn', {
allow: ['arrowFunctions'],
}],
'@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/no-misused-promises': ['error', {
checksVoidReturn: { arguments: false },
checksConditionals: true,
}],
'@typescript-eslint/no-unsafe-argument': 'error',
'@typescript-eslint/no-unsafe-assignment': 'error',
'@typescript-eslint/no-unsafe-call': 'error',
Expand Down Expand Up @@ -100,7 +105,10 @@ module.exports = tseslint.config(
'@typescript-eslint/no-empty-function': 'off', // sometimes necessary, otherwise not that harmful
'@typescript-eslint/no-explicit-any': 'off', // to much noise :(
'@typescript-eslint/no-floating-promises': 'off', // to much noise :(
'@typescript-eslint/no-misused-promises': 'warn',
'@typescript-eslint/no-misused-promises': ['warn', {
checksVoidReturn: { arguments: false },
checksConditionals: true,
}],
'@typescript-eslint/no-unsafe-argument': 'off', // useful, but some false positives
'@typescript-eslint/no-unsafe-assignment': 'off', // to many errors in existing code, some because @zvoove/components or @angular/components typing is bad
'@typescript-eslint/no-unsafe-call': 'off', // useful, but some false positives
Expand Down
8 changes: 3 additions & 5 deletions projects/components/core/src/time/time-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,12 @@ export abstract class ZvTimeAdapter<TTime> {
* @returns The deserialized time object, either a valid time, null if the value can be
* deserialized into a null time (e.g. the empty string), or an invalid time.
*/
deserialize(value: any): TTime | null {
deserialize(value: unknown): TTime | null {
if (!value) {
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
if (this.isTimeInstance(value) && this.isValid(value)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value;
if (this.isTimeInstance(value) && this.isValid(value as TTime)) {
return value as TTime;
}
if (typeof value === 'string') {
const matches = value.match(/^(\d{2}):(\d{2})$/);
Expand Down
4 changes: 2 additions & 2 deletions projects/components/core/src/time/time-formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { InjectionToken } from '@angular/core';

export interface ZvTimeFormats {
parse: {
timeInput: any;
timeInput: unknown;
};
display: {
timeInput: any;
timeInput: unknown;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
formControlName="time"
pattern="[0-9][0-9]?:?[0-9][0-9]?"
(focus)="_onFocus()"
(blur)="_onBlur(true)"
(blur)="_onBlur()"
(keydown)="_onTimeInputKeydown($event)"
#time
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/* eslint-disable @angular-eslint/no-conflicting-lifecycle --
Both DoCheck and OnChanges are required: OnChanges notifies MatFormField
of input changes via stateChanges.next(), while DoCheck runs
updateErrorState() which depends on parent form submission state that
cannot be observed reactively. This follows Angular Material's own
MatInput implementation. */
import { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';

import {
Expand Down Expand Up @@ -27,6 +33,7 @@ import {
NgControl,
NgForm,
ReactiveFormsModule,
ValidationErrors,
ValidatorFn,
Validators,
} from '@angular/forms';
Expand Down Expand Up @@ -167,7 +174,7 @@ export class ZvDateTimeInput<TDateTime, TDate, TTime>
timePlaceholder = this.dateTimeAdapter.timeAdapter.parseFormatExample();

/** `View -> model callback called when value changes` */
_onChange: (value: any) => void = () => {};
_onChange: (value: TDateTime | null) => void = () => {};

/** `View -> model callback called when input has been touched` */
_onTouched = () => {};
Expand Down Expand Up @@ -254,7 +261,7 @@ export class ZvDateTimeInput<TDateTime, TDate, TTime>
@ViewChild(MatDatepickerInput) public matDateInput!: MatDatepickerInput<TDate>;
@ViewChild(ZvTimeInput) public zvTimeInput!: ZvTimeInput<TTime>;
_childValidators: ValidatorFn[] = [(control) => this.matDateInput?.validate(control), (control) => this.zvTimeInput?.validate(control)];
validate(control: AbstractControl): Record<string, any> | null {
validate(control: AbstractControl): ValidationErrors | null {
const errors = this._childValidators.map((v) => v(control)).filter((error) => error);
if (!errors.length) {
if (this._form.value.time && !this._form.value.date) {
Expand All @@ -278,10 +285,8 @@ export class ZvDateTimeInput<TDateTime, TDate, TTime>
*
* @param value New value to be written to the model.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
writeValue(value: any): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
this._assignValue(value, { assignForm: true, emitChange: false });
writeValue(value: unknown): void {
this._assignValue(value as TDateTime | null, { assignForm: true, emitChange: false });
}

/**
Expand All @@ -291,8 +296,7 @@ export class ZvDateTimeInput<TDateTime, TDate, TTime>
*
* @param fn Callback to be triggered when the value changes.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
registerOnChange(fn: (value: any) => void): void {
registerOnChange(fn: (value: TDateTime | null) => void): void {
this._onChange = fn;
}

Expand Down Expand Up @@ -356,7 +360,7 @@ export class ZvDateTimeInput<TDateTime, TDate, TTime>
* Calls the touched callback only if the panel is closed. Otherwise, the trigger will
* "blur" to the panel when it opens, causing a false positive.
*/
_onBlur(_formatTime = false) {
_onBlur() {
this._focused = false;

if (!this.disabled) {
Expand Down
16 changes: 8 additions & 8 deletions projects/components/date-time-input/src/time-input.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
OnChanges,
OnDestroy,
Output,
Provider,
SimpleChanges,
forwardRef,
inject,
Expand Down Expand Up @@ -46,14 +47,14 @@ export class ZvTimeInputEvent<TTime> {
}

/** @docs-private */
export const ZV_TIME_VALUE_ACCESSOR: any = {
export const ZV_TIME_VALUE_ACCESSOR: Provider = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => ZvTimeInput),
multi: true,
};

/** @docs-private */
export const ZV_TIME_VALIDATORS: any = {
export const ZV_TIME_VALIDATORS: Provider = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => ZvTimeInput),
multi: true,
Expand Down Expand Up @@ -85,8 +86,7 @@ export class ZvTimeInput<TTime> implements ControlValueAccessor, AfterViewInit,
get value(): TTime | null {
return this._value;
}
set value(value: any) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
set value(value: TTime | null) {
this._assignValueProgrammatically(value);
}
protected _value: TTime | null = null;
Expand Down Expand Up @@ -130,7 +130,7 @@ export class ZvTimeInput<TTime> implements ControlValueAccessor, AfterViewInit,
_onTouched = () => {};
_validatorOnChange = () => {};

private _cvaOnChange: (value: any) => void = () => {};
private _cvaOnChange: (value: TTime | null) => void = () => {};
private _localeSubscription = Subscription.EMPTY;

/** The form control validator for whether the input parses. */
Expand Down Expand Up @@ -179,12 +179,12 @@ export class ZvTimeInput<TTime> implements ControlValueAccessor, AfterViewInit,
}

// Implemented as part of ControlValueAccessor.
writeValue(value: TTime): void {
this._assignValueProgrammatically(value);
writeValue(value: unknown): void {
this._assignValueProgrammatically(value as TTime | null);
}

// Implemented as part of ControlValueAccessor.
registerOnChange(fn: (value: any) => void): void {
registerOnChange(fn: (value: TTime | null) => void): void {
this._cvaOnChange = fn;
}

Expand Down
15 changes: 14 additions & 1 deletion projects/components/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,20 @@ module.exports = tseslint.config(
files: ["**/*.ts"],
rules: {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-unused-vars": ["warn", {
args: "all",
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
caughtErrorsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
ignoreRestSiblings: true,
}],
},
},
{
files: ["**/*.spec.ts"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
},
},
{
Expand Down
6 changes: 6 additions & 0 deletions projects/components/file-input/src/file-input.component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/* eslint-disable @angular-eslint/no-conflicting-lifecycle --
Both DoCheck and OnChanges are required: OnChanges notifies MatFormField
of input changes via stateChanges.next(), while DoCheck runs
updateErrorState() which depends on parent form submission state that
cannot be observed reactively. This follows Angular Material's own
MatInput implementation. */
import { coerceBooleanProperty } from '@angular/cdk/coercion';

import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,13 @@ export class ZvFlipContainer implements AfterViewInit {
this.show('back');
}

private _timerRef: any = 0;
private _timerRef: ReturnType<typeof setTimeout> | null = null;
public show(show: 'back' | 'front') {
if (this._active !== show) {
this._active = show;
this.cd.markForCheck();
this._flipStart();
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
clearTimeout(this._timerRef);
clearTimeout(this._timerRef!);
this._timerRef = setTimeout(() => {
this._flipDone();
}, 300);
Expand Down
1 change: 0 additions & 1 deletion projects/components/form-base/src/form.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { IZvFormError, IZvFormErrorData } from './models';

@Injectable({ providedIn: 'root' })
class TestZvFormService extends BaseZvFormService {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getLabel(_formControl: FormControl): Observable<string> | null {
return null;
}
Expand Down
2 changes: 0 additions & 2 deletions projects/components/form-base/src/form.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ export abstract class BaseZvFormService extends ZvFormService {
*/
public filterErrors(
errorData: IZvFormErrorData[],
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_includeControls: boolean,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_source: 'form' | 'control'
): Observable<IZvFormErrorData[]> {
return of(errorData);
Expand Down
18 changes: 8 additions & 10 deletions projects/components/form-base/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ export function hasRequiredField(abstractControl: AbstractControl): boolean {
}
}
if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const controls: any = abstractControl.controls; // any because of https://github.com/microsoft/TypeScript/issues/32552
for (const controlName in controls) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (controls[controlName]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
if (hasRequiredField(controls[controlName])) {
const controls = abstractControl.controls;
for (const control of Object.values(controls)) {
if (control) {
if (hasRequiredField(control)) {
return true;
}
}
Expand All @@ -29,9 +26,10 @@ export function hasRequiredField(abstractControl: AbstractControl): boolean {
*
* @param control The control class (MatSlider, MatSelect, ...)
*/
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment --
duck-typing against Angular Material internals with private fields (MatSlider._slider, _knobRadius, _step) */
export function getControlType(control: any): string | null {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const controlId: string = control.id /* MatFormFieldControl, z.B. checkbox */ || control.name; /* mat-radio-group */
const controlId: string = control.id /* MatFormFieldControl, z.B. checkbox */ || control.name /* mat-radio-group */ || '';
if (controlId) {
const parts = controlId.split('-');
if (parts[parts.length - 1].match(/[0-9]/)) {
Expand All @@ -40,10 +38,10 @@ export function getControlType(control: any): string | null {
return parts.join('-');
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (control._slider !== undefined || (control._knobRadius !== undefined && control._step !== undefined)) {
return 'mat-slider';
}

return null;
}
/* eslint-enable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment */
24 changes: 18 additions & 6 deletions projects/components/form-field/src/dummy-mat-form-field-control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,12 @@ export class DummyMatFormFieldControl implements MatFormFieldControl<string>, On
}
}

public onContainerClick(): void {}
public setDescribedByIds(): void {}
public onContainerClick(): void {
/* noop - required by MatFormFieldControl */
}
public setDescribedByIds(): void {
/* noop - required by MatFormFieldControl */
}

public onChange = () => {};
public onTouched = () => {};
Expand All @@ -92,11 +96,19 @@ export class DummyMatFormFieldControl implements MatFormFieldControl<string>, On
}
}

public writeValue() {}
public writeValue() {
/* noop - required by ControlValueAccessor */
}

public registerOnChange() {}
public registerOnChange() {
/* noop - required by ControlValueAccessor */
}

public registerOnTouched(): void {}
public registerOnTouched(): void {
/* noop - required by ControlValueAccessor */
}

public setDisabledState(): void {}
public setDisabledState(): void {
/* noop - required by ControlValueAccessor */
}
}
10 changes: 5 additions & 5 deletions projects/components/form-field/src/form-field.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class ZvFormField implements OnChanges, AfterContentChecked, OnDestroy {
this._labelChild = value;
this.updateLabel();
if (this._matFormField) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access -- accessing Angular Material internal _changeDetectorRef
(this._matFormField as any)._changeDetectorRef.markForCheck();
}
}
Expand Down Expand Up @@ -184,9 +184,9 @@ export class ZvFormField implements OnChanges, AfterContentChecked, OnDestroy {
this._matFormField._control = this.matFormFieldControl;
this.emulated = this.matFormFieldControl instanceof DummyMatFormFieldControl;
// This tells the mat-input that it is inside a mat-form-field
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- accessing Angular Material internal _isInFormField
if ((this.matFormFieldControl as any)._isInFormField !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- accessing Angular Material internal _isInFormField
(this.matFormFieldControl as any)._isInFormField = true;
}
this.realFormControl = getRealFormControl(this._ngControl, this.matFormFieldControl);
Expand All @@ -201,7 +201,7 @@ export class ZvFormField implements OnChanges, AfterContentChecked, OnDestroy {

if (this.formControl) {
if (this.formsService.tryDetectRequired) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- dynamically setting required on MatFormFieldControl
(this.matFormFieldControl as any).required = hasRequiredField(this.formControl);
}

Expand Down Expand Up @@ -264,7 +264,7 @@ export class ZvFormField implements OnChanges, AfterContentChecked, OnDestroy {

// when only our own component is marked for check, then the label will not be shown
// when labelText$ didn't run synchronously
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access -- accessing Angular Material internal _changeDetectorRef
(this._matFormField as any)._changeDetectorRef.markForCheck();
});
}
Expand Down
1 change: 0 additions & 1 deletion projects/components/form/src/form.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ describe('ZvForm', () => {
},
disconnect: () => {},
} as unknown as IntersectionObserver;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;

const fixture = TestBed.createComponent(TestDataSourceComponent);
Expand Down
Loading
Loading