From 4978a2110f0967b26aa09a05a91091721e19fb7d Mon Sep 17 00:00:00 2001 From: Maksim Ivanov Date: Tue, 27 Aug 2024 22:15:58 +0300 Subject: [PATCH] refactor(cdk): support noUncheckedIndexedAccess (#8620) --- .../input-card-group.providers.ts | 6 +++--- .../components/example/example.template.html | 2 +- .../navigation/navigation.component.ts | 2 +- .../table/directives/stuck.directive.ts | 2 +- projects/cdk/directives/pan/pan.service.ts | 4 ++-- projects/cdk/directives/swipe/swipe.service.ts | 12 ++++++------ projects/cdk/directives/zoom/zoom.service.ts | 8 ++++---- .../schematics/ng-add/steps/add-taiga-modules.ts | 12 ++++++++++-- .../ng-add/steps/wrap-with-tui-root.ts | 11 +++++++++-- .../ng-update/v4/steps/migrate-legacy-mask.ts | 4 ++-- .../v4/steps/migrate-number-format-settings.ts | 2 +- .../ng-update/v4/steps/restore-tui-mapper.ts | 2 +- .../ng-update/v4/steps/restore-tui-matcher.ts | 2 +- .../v4/steps/templates/migrate-overscroll.ts | 6 +++++- .../steps/templates/migrate-prevent-default.ts | 4 ++-- .../templates/migrate-progress-segmented.ts | 2 +- .../cdk/schematics/utils/add-unique-import.ts | 8 +++++--- .../utils/get-component-from-identifier.ts | 2 +- .../utils/templates/get-component-templates.ts | 6 ++++-- .../ng-component-input-manipulations.ts | 6 +++--- projects/cdk/utils/color/hex-to-rgba.ts | 4 ++-- projects/cdk/utils/color/parse-gradient.ts | 4 ++-- projects/cdk/utils/color/rgba-to-hex.ts | 16 ++++++++-------- projects/cdk/utils/focus/move-focus.ts | 2 +- .../miscellaneous/distance-between-touches.ts | 4 ++-- .../search/search-filters.component.ts | 8 +++++--- projects/testing/core/spin-button.harness.ts | 10 ++++++++-- 27 files changed, 91 insertions(+), 60 deletions(-) diff --git a/projects/addon-commerce/components/input-card-group/input-card-group.providers.ts b/projects/addon-commerce/components/input-card-group/input-card-group.providers.ts index 033788e93297..38e406fcdca7 100644 --- a/projects/addon-commerce/components/input-card-group/input-card-group.providers.ts +++ b/projects/addon-commerce/components/input-card-group/input-card-group.providers.ts @@ -42,9 +42,9 @@ export const TUI_INPUT_CARD_GROUP_TEXTS = tuiCreateTokenFromFactory< ]), ), map(([index, cardNumber, expiry, cvcTexts]) => ({ - cardNumberText: cardNumber[index], - expiryText: expiry[index], - cvcText: cvcTexts[index], + cardNumberText: cardNumber[index] ?? '', + expiryText: expiry[index] ?? '', + cvcText: cvcTexts[index] ?? '', })), ); }); diff --git a/projects/addon-doc/components/example/example.template.html b/projects/addon-doc/components/example/example.template.html index ad65ce024291..130f828530b2 100644 --- a/projects/addon-doc/components/example/example.template.html +++ b/projects/addon-doc/components/example/example.template.html @@ -95,7 +95,7 @@ diff --git a/projects/addon-doc/components/navigation/navigation.component.ts b/projects/addon-doc/components/navigation/navigation.component.ts index bdd3a14519cf..88a42769ac82 100644 --- a/projects/addon-doc/components/navigation/navigation.component.ts +++ b/projects/addon-doc/components/navigation/navigation.component.ts @@ -139,7 +139,7 @@ export class TuiDocNavigation { } protected get itemsWithoutSections(): TuiDocRoutePages { - return this.items[this.items.length - 1]; + return this.items[this.items.length - 1] ?? []; } protected $pages(pages: any): readonly TuiDocRoutePage[] { diff --git a/projects/addon-table/components/table/directives/stuck.directive.ts b/projects/addon-table/components/table/directives/stuck.directive.ts index 032ca1092e9a..2ea6178a6442 100644 --- a/projects/addon-table/components/table/directives/stuck.directive.ts +++ b/projects/addon-table/components/table/directives/stuck.directive.ts @@ -23,7 +23,7 @@ import {catchError, distinctUntilChanged, EMPTY, map} from 'rxjs'; export class TuiStuck { protected readonly stuck = toSignal( inject(IntersectionObserverService).pipe( - map((entries) => entries[entries.length - 1].intersectionRatio < 1), + map((entries) => (entries[entries.length - 1]?.intersectionRatio ?? 0) < 1), distinctUntilChanged(), tuiWatch(inject(ChangeDetectorRef)), catchError(() => EMPTY), // SSR diff --git a/projects/cdk/directives/pan/pan.service.ts b/projects/cdk/directives/pan/pan.service.ts index 324990b4b4c2..dfb010d02494 100644 --- a/projects/cdk/directives/pan/pan.service.ts +++ b/projects/cdk/directives/pan/pan.service.ts @@ -38,8 +38,8 @@ export class TuiPanService extends Observable { ), pairwise(), map(([first, second]) => { - const deltaX = second.clientX - first.clientX; - const deltaY = second.clientY - first.clientY; + const deltaX = (second?.clientX ?? 0) - (first?.clientX ?? 0); + const deltaY = (second?.clientY ?? 0) - (first?.clientY ?? 0); return [deltaX, deltaY] as [number, number]; }), diff --git a/projects/cdk/directives/swipe/swipe.service.ts b/projects/cdk/directives/swipe/swipe.service.ts index b734cf2719f3..c7f7b4a95d6f 100644 --- a/projects/cdk/directives/swipe/swipe.service.ts +++ b/projects/cdk/directives/swipe/swipe.service.ts @@ -25,14 +25,14 @@ export class TuiSwipeService extends Observable { filter( ([first, second]) => !!first.touches.length && - first.touches[0].identifier === - second.changedTouches[0].identifier, + first.touches[0]?.identifier === + second.changedTouches[0]?.identifier, ), map(([start, end]) => { - const startX = start.touches[0].clientX; - const startY = start.touches[0].clientY; - const endX = end.changedTouches[0].clientX; - const endY = end.changedTouches[0].clientY; + const startX = start.touches[0]?.clientX ?? 0; + const startY = start.touches[0]?.clientY ?? 0; + const endX = end.changedTouches[0]?.clientX ?? 0; + const endY = end.changedTouches[0]?.clientY ?? 0; const distanceX = startX - endX; const distanceY = startY - endY; diff --git a/projects/cdk/directives/zoom/zoom.service.ts b/projects/cdk/directives/zoom/zoom.service.ts index f85e460b1f79..ceb34c722f81 100644 --- a/projects/cdk/directives/zoom/zoom.service.ts +++ b/projects/cdk/directives/zoom/zoom.service.ts @@ -41,12 +41,12 @@ export class TuiZoomService extends Observable { ), map(({event, delta}) => { const clientX = - (event.touches[0].clientX + - event.touches[1].clientX) / + ((event.touches[0]?.clientX ?? 0) + + (event.touches[1]?.clientX ?? 0)) / 2; const clientY = - (event.touches[0].clientY + - event.touches[1].clientY) / + ((event.touches[0]?.clientY ?? 0) + + (event.touches[1]?.clientY ?? 0)) / 2; return {clientX, clientY, delta, event}; diff --git a/projects/cdk/schematics/ng-add/steps/add-taiga-modules.ts b/projects/cdk/schematics/ng-add/steps/add-taiga-modules.ts index e7004b27e3db..a245a265e9ab 100644 --- a/projects/cdk/schematics/ng-add/steps/add-taiga-modules.ts +++ b/projects/cdk/schematics/ng-add/steps/add-taiga-modules.ts @@ -65,12 +65,20 @@ function addTuiEntitiesToStandalone({ bootstrapOptions = bootstrapFunction.addArgument('{providers}: []'), ] = bootstrapFunction.getArguments(); + if (!rootComponentIdentifier) { + return; + } + const mainClass = getComponentFromIdentifier(rootComponentIdentifier); const optionsObject = getOptionsObject( bootstrapOptions as Identifier | ObjectLiteralExpression, ); + if (!optionsObject) { + return; + } + if (mainClass) { addMainModuleToRootComponent({mainClass, options, context}); addRootTuiProvidersToBootstrapFn(optionsObject); @@ -143,14 +151,14 @@ function getModules(extraModules?: ImportingModule[]): ImportingModule[] { function getOptionsObject( options: Identifier | ObjectLiteralExpression, -): ObjectLiteralExpression { +): ObjectLiteralExpression | null { if (Node.isObjectLiteralExpression(options)) { return options; } const definition = options.getDefinitionNodes()[0]; - return definition.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression)[0]; + return definition?.getChildrenOfKind(SyntaxKind.ObjectLiteralExpression)[0] ?? null; } export function addTaigaModules(options: TuiSchema): Rule { diff --git a/projects/cdk/schematics/ng-add/steps/wrap-with-tui-root.ts b/projects/cdk/schematics/ng-add/steps/wrap-with-tui-root.ts index beba3e2babba..484bfcfab092 100644 --- a/projects/cdk/schematics/ng-add/steps/wrap-with-tui-root.ts +++ b/projects/cdk/schematics/ng-add/steps/wrap-with-tui-root.ts @@ -53,9 +53,16 @@ function getAppTemplatePath(mainPath: string): string { if (standaloneBootstrapFunction) { const [componentIdentifier] = standaloneBootstrapFunction.getArguments(); - const component = getComponentFromIdentifier(componentIdentifier); - return (component && getTemplatePathFromComponent(component)) || ''; + if (componentIdentifier) { + const component = getComponentFromIdentifier(componentIdentifier); + + if (component) { + return getTemplatePathFromComponent(component); + } + } + + return ''; } const mainModule = getMainModule(mainPath); diff --git a/projects/cdk/schematics/ng-update/v4/steps/migrate-legacy-mask.ts b/projects/cdk/schematics/ng-update/v4/steps/migrate-legacy-mask.ts index d398b0974a9e..b8d81e168719 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/migrate-legacy-mask.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/migrate-legacy-mask.ts @@ -49,7 +49,7 @@ function migrateTuiMaskedMoneyValueIsEmpty(options: TuiSchema): void { const [value] = parent.getArguments(); parent.replaceWithText( - `Number.isNaN(maskitoParseNumber(${value.getText()}, ','))`, + `Number.isNaN(maskitoParseNumber(${value?.getText()}, ','))`, ); } }); @@ -82,7 +82,7 @@ function migrateTuiMaskedNumberStringToNumber(options: TuiSchema): void { const [value, decimalSeparator] = parent.getArguments(); parent.replaceWithText( - `maskitoParseNumber(${value.getText()}, ${decimalSeparator.getText()})`, + `maskitoParseNumber(${value?.getText()}, ${decimalSeparator?.getText()})`, ); } }); diff --git a/projects/cdk/schematics/ng-update/v4/steps/migrate-number-format-settings.ts b/projects/cdk/schematics/ng-update/v4/steps/migrate-number-format-settings.ts index 5064a0fe3fc5..87c19f3753f8 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/migrate-number-format-settings.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/migrate-number-format-settings.ts @@ -21,7 +21,7 @@ const OPTIONS_MIGRATIONS: Record< const [, propertyValue] = property.getText().split(/\s?:\s?/); property.replaceWithText( - propertyValue.match(/^['"`]never['"`]$/) + propertyValue?.match(/^['"`]never['"`]$/) ? 'precision: 0' : property.getText().replace('decimal', 'decimalMode'), ); diff --git a/projects/cdk/schematics/ng-update/v4/steps/restore-tui-mapper.ts b/projects/cdk/schematics/ng-update/v4/steps/restore-tui-mapper.ts index eea92f9b872b..955757dd95d1 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/restore-tui-mapper.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/restore-tui-mapper.ts @@ -35,7 +35,7 @@ function updateTuiMapper(options: TuiSchema): void { const [inputType] = typeArguments; - inputType.replaceWithText(`[${inputType.getText()}, ...any]`); + inputType?.replaceWithText(`[${inputType.getText()}, ...any]`); } } } diff --git a/projects/cdk/schematics/ng-update/v4/steps/restore-tui-matcher.ts b/projects/cdk/schematics/ng-update/v4/steps/restore-tui-matcher.ts index 07bc4fd7865a..13eb7f525ffa 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/restore-tui-matcher.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/restore-tui-matcher.ts @@ -35,7 +35,7 @@ function updateTuiMatcher(options: TuiSchema): void { const [inputType] = typeArguments; - inputType.replaceWithText(`[${inputType.getText()}, ...any]`); + inputType?.replaceWithText(`[${inputType.getText()}, ...any]`); } } } diff --git a/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-overscroll.ts b/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-overscroll.ts index afe590245cc4..1e58f208def0 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-overscroll.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-overscroll.ts @@ -46,7 +46,11 @@ export function migrateOverscroll({ ); }); - addTodo(recorder, elements[0].sourceCodeLocation as ElementLocation, templateOffset); + const element = elements[0]?.sourceCodeLocation as ElementLocation | undefined; + + if (element) { + addTodo(recorder, element, templateOffset); + } } function addTodo( diff --git a/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-prevent-default.ts b/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-prevent-default.ts index 6ed052bc6523..366fdec95f18 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-prevent-default.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-prevent-default.ts @@ -37,9 +37,9 @@ export function migratePreventDefault({ const event = preventDefaultAttr.value; const preventDefaultStart = - sourceCodeLocation?.attrs?.[preventDefaultAttr.name].startOffset || 0; + sourceCodeLocation?.attrs?.[preventDefaultAttr.name]?.startOffset || 0; const preventDefaultEnd = - sourceCodeLocation?.attrs?.[preventDefaultAttr.name].endOffset || 0; + sourceCodeLocation?.attrs?.[preventDefaultAttr.name]?.endOffset || 0; recorder.insertLeft( templateOffset + preventDefaultStart, diff --git a/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-progress-segmented.ts b/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-progress-segmented.ts index 0f165d89724d..01d22e8fc85a 100644 --- a/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-progress-segmented.ts +++ b/projects/cdk/schematics/ng-update/v4/steps/templates/migrate-progress-segmented.ts @@ -35,7 +35,7 @@ export function migrateProgressSegmented({ } const max = maxAttr.value; - const insertTo = sourceCodeLocation?.attrs?.[maxAttr.name].endOffset || 0; + const insertTo = sourceCodeLocation?.attrs?.[maxAttr.name]?.endOffset || 0; recorder.insertRight(insertTo + templateOffset, ` [segments]="${max}"`); }); diff --git a/projects/cdk/schematics/utils/add-unique-import.ts b/projects/cdk/schematics/utils/add-unique-import.ts index fcc209c95eee..dc5f58ec1468 100644 --- a/projects/cdk/schematics/utils/add-unique-import.ts +++ b/projects/cdk/schematics/utils/add-unique-import.ts @@ -18,12 +18,14 @@ export function addUniqueImport( moduleSpecifier, }); - if (existingDeclaration.length) { - const modules = existingDeclaration[0] + const imports = existingDeclaration?.[0]; + + if (imports) { + const modules = imports .getNamedImports() .map((namedImport) => namedImport.getText()); - editImports(existingDeclaration[0], () => ({ + editImports(imports, () => ({ namedImports: [...modules, namedImport], isTypeOnly: false, })); diff --git a/projects/cdk/schematics/utils/get-component-from-identifier.ts b/projects/cdk/schematics/utils/get-component-from-identifier.ts index 611f44bfa286..544f5f3c3588 100644 --- a/projects/cdk/schematics/utils/get-component-from-identifier.ts +++ b/projects/cdk/schematics/utils/get-component-from-identifier.ts @@ -9,7 +9,7 @@ export function getComponentFromIdentifier( })[0]; const rootComponentPath = - rootImportDeclaration.getModuleSpecifierSourceFile()?.getFilePath() || ''; + rootImportDeclaration?.getModuleSpecifierSourceFile()?.getFilePath() || ''; return getNgComponents(rootComponentPath, {name: identifier.getText()})[0]; } diff --git a/projects/cdk/schematics/utils/templates/get-component-templates.ts b/projects/cdk/schematics/utils/templates/get-component-templates.ts index 4e1799c90e53..d2d1763290c5 100644 --- a/projects/cdk/schematics/utils/templates/get-component-templates.ts +++ b/projects/cdk/schematics/utils/templates/get-component-templates.ts @@ -16,8 +16,10 @@ import type {TemplateResource} from '../../ng-update/interfaces/template-resourc function decoratorToTemplateResource(decorator: Decorator): TemplateResource | null { const [metadata] = decorator.getArguments() as ObjectLiteralExpression[]; - const templateUrl = metadata.getProperty('templateUrl') as PropertyAssignment; - const template = metadata.getProperty('template') as PropertyAssignment; + const templateUrl = metadata?.getProperty('templateUrl') as + | PropertyAssignment + | undefined; + const template = metadata?.getProperty('template') as PropertyAssignment | undefined; const componentPath = decorator.getSourceFile().getFilePath(); if (templateUrl) { diff --git a/projects/cdk/schematics/utils/templates/ng-component-input-manipulations.ts b/projects/cdk/schematics/utils/templates/ng-component-input-manipulations.ts index b462720d9c23..95248a08c1dd 100644 --- a/projects/cdk/schematics/utils/templates/ng-component-input-manipulations.ts +++ b/projects/cdk/schematics/utils/templates/ng-component-input-manipulations.ts @@ -93,8 +93,8 @@ export function replaceInputProperty({ }); propertyValues.forEach(([startOffset, endOffset]) => { - recorder.remove(startOffset, endOffset - startOffset); - recorder.insertRight(startOffset, newValue); + recorder.remove(startOffset ?? 0, (endOffset ?? 0) - (startOffset ?? 0)); + recorder.insertRight(startOffset ?? 0, newValue); }); return true; @@ -229,6 +229,6 @@ export function removeInputProperty({ ].map(([start, end]) => [templateOffset + start, templateOffset + end]); propertyOffsets.forEach(([start, end]) => { - recorder.remove(start, end - start); + recorder.remove(start ?? 0, (end ?? 0) - (start ?? 0)); }); } diff --git a/projects/cdk/utils/color/hex-to-rgba.ts b/projects/cdk/utils/color/hex-to-rgba.ts index 46c1dfe5ad6f..a8c24a206d73 100644 --- a/projects/cdk/utils/color/hex-to-rgba.ts +++ b/projects/cdk/utils/color/hex-to-rgba.ts @@ -4,7 +4,7 @@ const getChunksFromString = (hex: string, chunkSize: number): RegExpMatchArray | const convertHexUnitTo256 = (hexStr: string): number => parseInt(hexStr.repeat(2 / hexStr.length), 16); -const getAlphaFloat = (a: number, alpha?: number): number => { +const getAlphaFloat = (a?: number, alpha?: number): number => { if (typeof a !== 'undefined') { return Number((a / 255).toFixed(2)); } @@ -36,7 +36,7 @@ export function tuiParseHex( const chunkSize = Math.floor((hex.length - 1) / 3); const hexArr = getChunksFromString(hex.slice(1), chunkSize); - const [r, g, b, a] = hexArr?.map(convertHexUnitTo256) ?? []; + const [r = NaN, g = NaN, b = NaN, a] = hexArr?.map(convertHexUnitTo256) ?? []; const floatAlpha = getAlphaFloat(a, alpha); return [r, g, b, floatAlpha]; diff --git a/projects/cdk/utils/color/parse-gradient.ts b/projects/cdk/utils/color/parse-gradient.ts index 882a16bc15d7..a0ea4dc2fb6e 100644 --- a/projects/cdk/utils/color/parse-gradient.ts +++ b/projects/cdk/utils/color/parse-gradient.ts @@ -77,8 +77,8 @@ export function tuiParseGradient(input: string): TuiParsedGradient { while (matchColorStop !== null) { stops = stops.concat({ - color: matchColorStop[1], - position: getPosition(matchColorStop[2], stops.length), + color: matchColorStop[1] || '', + position: getPosition(matchColorStop[2] || '', stops.length), }); matchColorStop = stopsRegexp.exec(stopsString); diff --git a/projects/cdk/utils/color/rgba-to-hex.ts b/projects/cdk/utils/color/rgba-to-hex.ts index d6984c872175..78a7b240084e 100644 --- a/projects/cdk/utils/color/rgba-to-hex.ts +++ b/projects/cdk/utils/color/rgba-to-hex.ts @@ -3,16 +3,16 @@ export function tuiRgbaToHex(color: string): string { throw new Error('Invalid RGBa'); } - const rgb: number[] = - (color - .replaceAll(/\s/g, '') - .match(/^rgba?\((\d+),(\d+),(\d+),?([^,\s)]+)?/i) as unknown as number[]) ?? - []; + const rgb = + color.replaceAll(/\s/g, '').match(/^rgba?\((\d+),(\d+),(\d+),?([^,\s)]+)?/i) ?? + null; + let alpha: number | string = (rgb?.[4] ?? '').toString().trim(); + let hex = rgb - ? (rgb[1] | (1 << 8)).toString(16).slice(1) + - (rgb[2] | (1 << 8)).toString(16).slice(1) + - (rgb[3] | (1 << 8)).toString(16).slice(1) + ? ((parseInt(rgb?.[1] ?? '', 10) || 0) | (1 << 8)).toString(16).slice(1) + + ((parseInt(rgb?.[2] ?? '', 10) || 0) | (1 << 8)).toString(16).slice(1) + + ((parseInt(rgb?.[3] ?? '', 10) || 0) | (1 << 8)).toString(16).slice(1) : color; alpha = alpha !== '' ? alpha : 0o1; diff --git a/projects/cdk/utils/focus/move-focus.ts b/projects/cdk/utils/focus/move-focus.ts index a457a9953df3..af68ab7db872 100644 --- a/projects/cdk/utils/focus/move-focus.ts +++ b/projects/cdk/utils/focus/move-focus.ts @@ -15,7 +15,7 @@ export function tuiMoveFocus( currentIndex += step; while (currentIndex >= 0 && currentIndex < elements.length) { - elements[currentIndex].focus(); + elements[currentIndex]?.focus(); if (tuiIsNativeFocused(elements[currentIndex])) { return; diff --git a/projects/cdk/utils/miscellaneous/distance-between-touches.ts b/projects/cdk/utils/miscellaneous/distance-between-touches.ts index 3491d4bcb771..ab102e993d39 100644 --- a/projects/cdk/utils/miscellaneous/distance-between-touches.ts +++ b/projects/cdk/utils/miscellaneous/distance-between-touches.ts @@ -1,6 +1,6 @@ export function tuiDistanceBetweenTouches({touches}: TouchEvent): number { return Math.hypot( - touches[0].clientX - touches[1].clientX, - touches[0].clientY - touches[1].clientY, + (touches[0]?.clientX ?? 0) - (touches[1]?.clientX ?? 0), + (touches[0]?.clientY ?? 0) - (touches[1]?.clientY ?? 0), ); } diff --git a/projects/layout/components/search/search-filters.component.ts b/projects/layout/components/search/search-filters.component.ts index 4e734aec20ef..950e647af007 100644 --- a/projects/layout/components/search/search-filters.component.ts +++ b/projects/layout/components/search/search-filters.component.ts @@ -69,9 +69,11 @@ export class TuiSearchFiltersComponent implements AfterContentInit { protected readonly overflown = toSignal( inject(ResizeObserverService, {self: true}).pipe( - map(([{contentRect}]) => - Math.floor((contentRect.width - this.more) / WIDTH / this.unit), - ), + map((entry) => { + const width = entry[0]?.contentRect.width ?? 0; + + return Math.floor((width - this.more) / WIDTH / this.unit); + }), distinctUntilChanged(), tuiZonefull(inject(NgZone)), ), diff --git a/projects/testing/core/spin-button.harness.ts b/projects/testing/core/spin-button.harness.ts index 31f232c3247d..e578b098701b 100644 --- a/projects/testing/core/spin-button.harness.ts +++ b/projects/testing/core/spin-button.harness.ts @@ -6,10 +6,16 @@ export class TuiSpinButtonHarness extends TuiComponentHarness { public static hostSelector = 'tui-spin-button'; public async isLeftDisabled(): Promise { - return (await this.locatorForAll(TuiButtonHarness)())[0].hasClass('t-hidden'); + return ( + (await this.locatorForAll(TuiButtonHarness)())[0]?.hasClass('t-hidden') ?? + false + ); } public async isRightDisabled(): Promise { - return (await this.locatorForAll(TuiButtonHarness)())[1].hasClass('t-hidden'); + return ( + (await this.locatorForAll(TuiButtonHarness)())[1]?.hasClass('t-hidden') ?? + false + ); } }