From 5eefc36d6d0ef82deb0ee8f91e82fa652bc84979 Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Tue, 1 Jul 2025 12:10:46 +0300 Subject: [PATCH 1/7] fix(simple-combo): Adding disableFiltering to simple-combo --- .../simple-combo.component.spec.ts | 24 +++++++++++++++++++ .../simple-combo/simple-combo.component.ts | 22 +++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts index de80ac92df3..3acabb94b53 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts @@ -1230,6 +1230,30 @@ describe('IgxSimpleCombo', () => { expect(combo.displayValue).toEqual('Wisconsin'); }); + it('should not filter the data when disableFiltering is true', () => { + combo.disableFiltering = true; + fixture.detectChanges(); + combo.focusSearchInput(); + expect(combo.filteredData.length).toEqual(combo.data.length); + + UIInteractions.simulateTyping('con', input); + expect(combo.comboInput.value).toEqual('con'); + fixture.detectChanges(); + + expect(combo.filteredData.length).toEqual(combo.data.length); + UIInteractions.triggerEventHandlerKeyDown('Tab', input); + fixture.detectChanges(); + + combo.disableFiltering = false; + fixture.detectChanges(); + combo.focusSearchInput(); + expect(combo.filteredData.length).toEqual(combo.data.length); + UIInteractions.simulateTyping('con', input); + expect(combo.comboInput.value).toEqual('con'); + fixture.detectChanges(); + expect(combo.filteredData.length).toEqual(2); + }); + it('should display the AddItem button when allowCustomValues is true and there is a partial match', fakeAsync(() => { fixture.componentInstance.allowCustomValues = true; fixture.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts index 4eb2ac7f052..03e7ecb6d20 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts @@ -1,7 +1,9 @@ import { NgTemplateOutlet } from '@angular/common'; import { AfterViewInit, ChangeDetectorRef, Component, DoCheck, ElementRef, EventEmitter, HostListener, Inject, Injector, - Optional, Output, ViewChild, DOCUMENT + Optional, Output, ViewChild, DOCUMENT, + Input, + booleanAttribute } from '@angular/core'; import { ControlValueAccessor, FormGroupDirective, NG_VALUE_ACCESSOR } from '@angular/forms'; import { takeUntil } from 'rxjs/operators'; @@ -114,9 +116,22 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co private _collapsing = false; + private _disableFiltering = false; + + /** + * Enables/disables filtering in the list. The default is `false`. + */ + @Input({ transform: booleanAttribute }) + public get disableFiltering(): boolean { + return this._disableFiltering; + } + public set disableFiltering(value: boolean) { + this._disableFiltering = value; + } + /** @hidden @internal */ public get filteredData(): any[] | null { - return this._filteredData; + return this.disableFiltering ? this.data : this._filteredData; } /** @hidden @internal */ public set filteredData(val: any[] | null) { @@ -290,6 +305,9 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co if (this.collapsed && this.comboInput.focused) { this.open(); } + if (this.disableFiltering) { + return; + } if (event !== undefined) { this.filterValue = this.searchValue = typeof event === 'string' ? event : event.target.value; } From 7a479ed6db3824c27594ac078505dd0c23a18f40 Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Fri, 4 Jul 2025 16:13:13 +0300 Subject: [PATCH 2/7] chore(combo): Moving the disableFiltering property to combo.common --- CHANGELOG.md | 4 ++++ .../igniteui-angular/src/lib/combo/combo.common.ts | 12 ++++++++++++ .../src/lib/combo/combo.component.ts | 11 ----------- .../lib/simple-combo/simple-combo.component.html | 2 +- .../src/lib/simple-combo/simple-combo.component.ts | 13 ------------- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe5277e2a5..31392de5763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,10 @@ All notable changes for each version of this project will be documented in this ## 19.2.0 +### New Features +- `IgxSimpleCombo` + - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. + ### General - `IgxCarousel` - Removed deprecated property `keyboardSupport`. diff --git a/projects/igniteui-angular/src/lib/combo/combo.common.ts b/projects/igniteui-angular/src/lib/combo/combo.common.ts index 6084b0ef637..45d877cc4a6 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.common.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.common.ts @@ -123,6 +123,17 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh @Input({ transform: booleanAttribute }) public showSearchCaseIcon = false; + /** + * Enables/disables filtering in the list. The default is `false`. + */ + @Input({ transform: booleanAttribute }) + public get disableFiltering(): boolean { + return this._disableFiltering; + } + public set disableFiltering(value: boolean) { + this._disableFiltering = value; + } + /** * Set custom overlay settings that control how the combo's list of items is displayed. * Set: @@ -945,6 +956,7 @@ export abstract class IgxComboBaseDirective implements IgxComboBase, AfterViewCh protected computedStyles; private _id: string = `igx-combo-${NEXT_ID++}`; + private _disableFiltering = false; private _type = null; private _dataType = ''; private _itemHeight = undefined; diff --git a/projects/igniteui-angular/src/lib/combo/combo.component.ts b/projects/igniteui-angular/src/lib/combo/combo.component.ts index f3b0d6add53..911d737e8bb 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.component.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.component.ts @@ -128,16 +128,6 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie @Input({ transform: booleanAttribute }) public autoFocusSearch = true; - /** - * Enables/disables filtering in the list. The default is `false`. - */ - @Input({ transform: booleanAttribute }) - public get disableFiltering(): boolean { - return this._disableFiltering || this.filteringOptions.filterable === false; - } - public set disableFiltering(value: boolean) { - this._disableFiltering = value; - } /** * Defines the placeholder value for the combo dropdown search field @@ -184,7 +174,6 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie protected _prevInputValue = ''; private _displayText: string; - private _disableFiltering = false; constructor( elementRef: ElementRef, diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.html b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.html index a5d56f0cd59..53b343f484b 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.html +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.html @@ -68,7 +68,7 @@ (focus)="dropdown.onFocus()" (keydown)="handleItemKeyDown($event)"> diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts index 03e7ecb6d20..b053d3b92b2 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts @@ -116,19 +116,6 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co private _collapsing = false; - private _disableFiltering = false; - - /** - * Enables/disables filtering in the list. The default is `false`. - */ - @Input({ transform: booleanAttribute }) - public get disableFiltering(): boolean { - return this._disableFiltering; - } - public set disableFiltering(value: boolean) { - this._disableFiltering = value; - } - /** @hidden @internal */ public get filteredData(): any[] | null { return this.disableFiltering ? this.data : this._filteredData; From 05c226c3786b51f24683325edc63ef0b21c7ec2e Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Fri, 4 Jul 2025 18:39:00 +0300 Subject: [PATCH 3/7] chore(combo): Updating changelog and removing unnecessary code --- CHANGELOG.md | 17 +++++++++++++---- .../simple-combo/simple-combo.component.spec.ts | 4 ++-- .../lib/simple-combo/simple-combo.component.ts | 9 ++------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31392de5763..b8512874eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes for each version of this project will be documented in this file. +## 20.0.5 +### General +- `IgxSimpleCombo` + - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. With this introduction, `filteringOptions.filterable` is no longer usde. +- `IgxCombo`, `IgxSimpleCombo` + - Removed deprecated `filteringOptions.filterable` option. + ## 20.0.2 ### New Features @@ -41,12 +48,14 @@ All notable changes for each version of this project will be documented in this ``` -## 19.2.0 - -### New Features +## 19.2.14 +### General - `IgxSimpleCombo` - - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. + - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. With this introduction, `filteringOptions.filterable` is no longer usde. +- `IgxCombo`, `IgxSimpleCombo` + - Removed deprecated `filteringOptions.filterable` option. +## 19.2.0 ### General - `IgxCarousel` - Removed deprecated property `keyboardSupport`. diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts index 3acabb94b53..d14691194bb 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.spec.ts @@ -1234,7 +1234,6 @@ describe('IgxSimpleCombo', () => { combo.disableFiltering = true; fixture.detectChanges(); combo.focusSearchInput(); - expect(combo.filteredData.length).toEqual(combo.data.length); UIInteractions.simulateTyping('con', input); expect(combo.comboInput.value).toEqual('con'); @@ -1247,7 +1246,8 @@ describe('IgxSimpleCombo', () => { combo.disableFiltering = false; fixture.detectChanges(); combo.focusSearchInput(); - expect(combo.filteredData.length).toEqual(combo.data.length); + combo.comboInput.value = ''; + fixture.detectChanges(); UIInteractions.simulateTyping('con', input); expect(combo.comboInput.value).toEqual('con'); fixture.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts index b053d3b92b2..4eb2ac7f052 100644 --- a/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts +++ b/projects/igniteui-angular/src/lib/simple-combo/simple-combo.component.ts @@ -1,9 +1,7 @@ import { NgTemplateOutlet } from '@angular/common'; import { AfterViewInit, ChangeDetectorRef, Component, DoCheck, ElementRef, EventEmitter, HostListener, Inject, Injector, - Optional, Output, ViewChild, DOCUMENT, - Input, - booleanAttribute + Optional, Output, ViewChild, DOCUMENT } from '@angular/core'; import { ControlValueAccessor, FormGroupDirective, NG_VALUE_ACCESSOR } from '@angular/forms'; import { takeUntil } from 'rxjs/operators'; @@ -118,7 +116,7 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co /** @hidden @internal */ public get filteredData(): any[] | null { - return this.disableFiltering ? this.data : this._filteredData; + return this._filteredData; } /** @hidden @internal */ public set filteredData(val: any[] | null) { @@ -292,9 +290,6 @@ export class IgxSimpleComboComponent extends IgxComboBaseDirective implements Co if (this.collapsed && this.comboInput.focused) { this.open(); } - if (this.disableFiltering) { - return; - } if (event !== undefined) { this.filterValue = this.searchValue = typeof event === 'string' ? event : event.target.value; } From eac230967f46f9373921d3e956510163776560cb Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Mon, 7 Jul 2025 15:37:02 +0300 Subject: [PATCH 4/7] chore(simple-combo): Adding migrations for disableFiltering --- .../migrations/migration-collection.json | 5 + .../update-20_0_5/changes/inputs.json | 13 +++ .../migrations/update-20_0_5/index.spec.ts | 53 +++++++++ .../migrations/update-20_0_5/index.ts | 102 ++++++++++++++++++ 4 files changed, 173 insertions(+) create mode 100644 projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json create mode 100644 projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts create mode 100644 projects/igniteui-angular/migrations/update-20_0_5/index.ts diff --git a/projects/igniteui-angular/migrations/migration-collection.json b/projects/igniteui-angular/migrations/migration-collection.json index a151f709c6c..54f55431773 100644 --- a/projects/igniteui-angular/migrations/migration-collection.json +++ b/projects/igniteui-angular/migrations/migration-collection.json @@ -236,6 +236,11 @@ "version": "20.0.2", "description": "Updates Ignite UI for Angular from v20.0.0 to v20.0.2", "factory": "./update-20_0_2" + }, + "migration-48": { + "version": "20.0.5", + "description": "Updates Ignite UI for Angular from v20.0.2 to v20.0.5", + "factory": "./update-20_0_5" } } } diff --git a/projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json b/projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json new file mode 100644 index 00000000000..7636cb0080f --- /dev/null +++ b/projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json @@ -0,0 +1,13 @@ +{ + "$schema": "../../common/schema/binding.schema.json", + "changes": [ + { + "name": "filteringOptions.filterable", + "replaceWith": "disableFiltering", + "owner": { + "selector": "igx-simple-combo", + "type": "component" + } + } + ] +} diff --git a/projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts b/projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts new file mode 100644 index 00000000000..436ccd0f3e5 --- /dev/null +++ b/projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts @@ -0,0 +1,53 @@ +import * as path from 'path'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { setupTestTree } from '../common/setup.spec'; + +describe('Migration 20.0.5 - Replace filteringOptions.filterable', () => { + let appTree: UnitTestTree; + const runner = new SchematicTestRunner( + 'ig-migrate', + path.join(__dirname, '../migration-collection.json') + ); + const migrationName = 'migration-48'; + const makeTemplate = (name: string) => `/testSrc/appPrefix/component/${name}.component.html`; + + beforeEach(() => { + appTree = setupTestTree(); + }); + + it('should replace simple inline filteringOptions', async () => { + const input = ``; + appTree.create(makeTemplate('inline1'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate('inline1')); + + expect(output).toContain(`[disableFiltering]="false"`); + expect(output).not.toContain('filterable'); + }); + + it('should handle mixed object literal correctly', async () => { + const input = ``; + appTree.create(makeTemplate('inline2'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate('inline2')); + + expect(output).toContain(`[disableFiltering]="true"`); + expect(output).toContain(`[filteringOptions]="{ caseSensitive: true }"`); + }); + + it('should warn on variable reference', async () => { + const input = ``; + const warnMsg = "Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + + "If you were using filteringOptions please include them without 'filterable'"; + + appTree.create(makeTemplate('referenceInTsFile'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate('referenceInTsFile')); + + expect(output).not.toContain('[filteringOptions]'); + expect(output).toContain(warnMsg); + }); +}); diff --git a/projects/igniteui-angular/migrations/update-20_0_5/index.ts b/projects/igniteui-angular/migrations/update-20_0_5/index.ts new file mode 100644 index 00000000000..98ff8654d85 --- /dev/null +++ b/projects/igniteui-angular/migrations/update-20_0_5/index.ts @@ -0,0 +1,102 @@ +import { Element } from '@angular/compiler'; +import type { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { UpdateChanges } from '../common/UpdateChanges'; +import { + FileChange, + findElementNodes, + getSourceOffset, + parseFile +} from '../common/util'; +import { nativeImport } from 'igniteui-angular/migrations/common/import-helper.js'; + +const version = '20.0.5'; + +export default (): Rule => async (host: Tree, context: SchematicContext) => { + context.logger.info( + `Applying migration for Ignite UI for Angular to version ${version}` + ); + + const { HtmlParser } = await nativeImport('@angular/compiler') as typeof import('@angular/compiler'); + + const update = new UpdateChanges(__dirname, host, context); + const changes = new Map(); + const parser = new HtmlParser(); + + const warnMsg = "Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + + "If you were using filteringOptions please include them without 'filterable'"; + + const applyChanges = () => { + for (const [path, fileChanges] of changes.entries()) { + let content = host.read(path).toString(); + fileChanges.sort((a, b) => b.position - a.position).forEach(c => { + content = c.apply(content); + }); + host.overwrite(path, content); + } + }; + + const addChange = (path: string, change: FileChange) => { + if (!changes.has(path)) { + changes.set(path, []); + } + changes.get(path).push(change); + }; + + const COMBO_TAG = 'igx-simple-combo'; + + for (const path of update.templateFiles) { + const nodes = findElementNodes(parseFile(parser, host, path), COMBO_TAG); + + for (const node of nodes) { + if (!(node instanceof Element)) continue; + + const attr = node.attrs.find(a => a.name === '[filteringOptions]'); + if (!attr) continue; + + const attrVal = attr.value.trim(); + const offset = getSourceOffset(node); + const file = offset.file; + + let replacementText = ''; + + if (attrVal.startsWith('{')) { + // inline object literal + const parsed = eval('(' + attrVal + ')'); + const filterable = parsed.filterable; + + if (typeof filterable === 'boolean') { + replacementText += `[disableFiltering]="${!filterable}"`; + } + + const remaining = { ...parsed }; + delete remaining.filterable; + const remainingProps = Object.entries(remaining) + .map(([k, v]) => `${k}: ${JSON.stringify(v)}`) + .join(', '); + + if (remainingProps.length > 0) { + replacementText += ` [filteringOptions]="{ ${remainingProps} }"`; + } + + // Replace whole [filteringOptions] attribute + const match = node.sourceSpan.toString().match(/\[filteringOptions\]="([^"]+)"/); + if (match) { + const attrText = match[0]; + const attrPos = file.content.indexOf(attrText, offset.startTag.start); + addChange(file.url, new FileChange(attrPos, replacementText, attrText, 'replace')); + } + } else { + // log for manual TS edit + const attrText = `[filteringOptions]="${attrVal}"`; + const attrPos = file.content.indexOf(attrText, offset.startTag.start); + + addChange(file.url, new FileChange(attrPos, '', attrText, 'replace')); + addChange(file.url, new FileChange(offset.startTag.end, warnMsg)); + } + } + } + + applyChanges(); + + update.applyChanges(); +}; From fd5e65e5bc453c3c3f50217932b62a77aa934b5b Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Tue, 8 Jul 2025 19:04:08 +0300 Subject: [PATCH 5/7] chore(combo): Adding additional custom migration for ts files --- CHANGELOG.md | 2 +- .../migrations/migration-collection.json | 4 +- .../update-20_0_5/changes/inputs.json | 13 -- .../migrations/update-20_0_5/index.spec.ts | 53 ----- .../migrations/update-20_0_5/index.ts | 102 --------- .../migrations/update-20_0_6/index.spec.ts | 209 ++++++++++++++++++ .../migrations/update-20_0_6/index.ts | 202 +++++++++++++++++ .../src/lib/combo/combo.common.ts | 5 - 8 files changed, 414 insertions(+), 176 deletions(-) delete mode 100644 projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json delete mode 100644 projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts delete mode 100644 projects/igniteui-angular/migrations/update-20_0_5/index.ts create mode 100644 projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts create mode 100644 projects/igniteui-angular/migrations/update-20_0_6/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index b8512874eac..a7aeb86f9c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes for each version of this project will be documented in this ## 20.0.5 ### General - `IgxSimpleCombo` - - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. With this introduction, `filteringOptions.filterable` is no longer usde. + - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. - `IgxCombo`, `IgxSimpleCombo` - Removed deprecated `filteringOptions.filterable` option. diff --git a/projects/igniteui-angular/migrations/migration-collection.json b/projects/igniteui-angular/migrations/migration-collection.json index 54f55431773..0d4a1970d3a 100644 --- a/projects/igniteui-angular/migrations/migration-collection.json +++ b/projects/igniteui-angular/migrations/migration-collection.json @@ -238,9 +238,9 @@ "factory": "./update-20_0_2" }, "migration-48": { - "version": "20.0.5", + "version": "20.0.6", "description": "Updates Ignite UI for Angular from v20.0.2 to v20.0.5", - "factory": "./update-20_0_5" + "factory": "./update-20_0_6" } } } diff --git a/projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json b/projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json deleted file mode 100644 index 7636cb0080f..00000000000 --- a/projects/igniteui-angular/migrations/update-20_0_5/changes/inputs.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "../../common/schema/binding.schema.json", - "changes": [ - { - "name": "filteringOptions.filterable", - "replaceWith": "disableFiltering", - "owner": { - "selector": "igx-simple-combo", - "type": "component" - } - } - ] -} diff --git a/projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts b/projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts deleted file mode 100644 index 436ccd0f3e5..00000000000 --- a/projects/igniteui-angular/migrations/update-20_0_5/index.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import * as path from 'path'; -import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; -import { setupTestTree } from '../common/setup.spec'; - -describe('Migration 20.0.5 - Replace filteringOptions.filterable', () => { - let appTree: UnitTestTree; - const runner = new SchematicTestRunner( - 'ig-migrate', - path.join(__dirname, '../migration-collection.json') - ); - const migrationName = 'migration-48'; - const makeTemplate = (name: string) => `/testSrc/appPrefix/component/${name}.component.html`; - - beforeEach(() => { - appTree = setupTestTree(); - }); - - it('should replace simple inline filteringOptions', async () => { - const input = ``; - appTree.create(makeTemplate('inline1'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeTemplate('inline1')); - - expect(output).toContain(`[disableFiltering]="false"`); - expect(output).not.toContain('filterable'); - }); - - it('should handle mixed object literal correctly', async () => { - const input = ``; - appTree.create(makeTemplate('inline2'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeTemplate('inline2')); - - expect(output).toContain(`[disableFiltering]="true"`); - expect(output).toContain(`[filteringOptions]="{ caseSensitive: true }"`); - }); - - it('should warn on variable reference', async () => { - const input = ``; - const warnMsg = "Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + - "If you were using filteringOptions please include them without 'filterable'"; - - appTree.create(makeTemplate('referenceInTsFile'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeTemplate('referenceInTsFile')); - - expect(output).not.toContain('[filteringOptions]'); - expect(output).toContain(warnMsg); - }); -}); diff --git a/projects/igniteui-angular/migrations/update-20_0_5/index.ts b/projects/igniteui-angular/migrations/update-20_0_5/index.ts deleted file mode 100644 index 98ff8654d85..00000000000 --- a/projects/igniteui-angular/migrations/update-20_0_5/index.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Element } from '@angular/compiler'; -import type { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; -import { UpdateChanges } from '../common/UpdateChanges'; -import { - FileChange, - findElementNodes, - getSourceOffset, - parseFile -} from '../common/util'; -import { nativeImport } from 'igniteui-angular/migrations/common/import-helper.js'; - -const version = '20.0.5'; - -export default (): Rule => async (host: Tree, context: SchematicContext) => { - context.logger.info( - `Applying migration for Ignite UI for Angular to version ${version}` - ); - - const { HtmlParser } = await nativeImport('@angular/compiler') as typeof import('@angular/compiler'); - - const update = new UpdateChanges(__dirname, host, context); - const changes = new Map(); - const parser = new HtmlParser(); - - const warnMsg = "Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + - "If you were using filteringOptions please include them without 'filterable'"; - - const applyChanges = () => { - for (const [path, fileChanges] of changes.entries()) { - let content = host.read(path).toString(); - fileChanges.sort((a, b) => b.position - a.position).forEach(c => { - content = c.apply(content); - }); - host.overwrite(path, content); - } - }; - - const addChange = (path: string, change: FileChange) => { - if (!changes.has(path)) { - changes.set(path, []); - } - changes.get(path).push(change); - }; - - const COMBO_TAG = 'igx-simple-combo'; - - for (const path of update.templateFiles) { - const nodes = findElementNodes(parseFile(parser, host, path), COMBO_TAG); - - for (const node of nodes) { - if (!(node instanceof Element)) continue; - - const attr = node.attrs.find(a => a.name === '[filteringOptions]'); - if (!attr) continue; - - const attrVal = attr.value.trim(); - const offset = getSourceOffset(node); - const file = offset.file; - - let replacementText = ''; - - if (attrVal.startsWith('{')) { - // inline object literal - const parsed = eval('(' + attrVal + ')'); - const filterable = parsed.filterable; - - if (typeof filterable === 'boolean') { - replacementText += `[disableFiltering]="${!filterable}"`; - } - - const remaining = { ...parsed }; - delete remaining.filterable; - const remainingProps = Object.entries(remaining) - .map(([k, v]) => `${k}: ${JSON.stringify(v)}`) - .join(', '); - - if (remainingProps.length > 0) { - replacementText += ` [filteringOptions]="{ ${remainingProps} }"`; - } - - // Replace whole [filteringOptions] attribute - const match = node.sourceSpan.toString().match(/\[filteringOptions\]="([^"]+)"/); - if (match) { - const attrText = match[0]; - const attrPos = file.content.indexOf(attrText, offset.startTag.start); - addChange(file.url, new FileChange(attrPos, replacementText, attrText, 'replace')); - } - } else { - // log for manual TS edit - const attrText = `[filteringOptions]="${attrVal}"`; - const attrPos = file.content.indexOf(attrText, offset.startTag.start); - - addChange(file.url, new FileChange(attrPos, '', attrText, 'replace')); - addChange(file.url, new FileChange(offset.startTag.end, warnMsg)); - } - } - } - - applyChanges(); - - update.applyChanges(); -}; diff --git a/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts b/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts new file mode 100644 index 00000000000..71d960d15cd --- /dev/null +++ b/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts @@ -0,0 +1,209 @@ +import * as path from 'path'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { setupTestTree } from '../common/setup.spec'; + +describe('Migration 20.0.6 - Replace filteringOptions.filterable', () => { + let appTree: UnitTestTree; + const runner = new SchematicTestRunner( + 'ig-migrate', + path.join(__dirname, '../migration-collection.json') + ); + const migrationName = 'migration-48'; + const makeTemplate = (name: string) => `/testSrc/appPrefix/component/${name}.component.html`; + const makeScript = (name: string) => `/testSrc/appPrefix/component/${name}.component.ts`; + const components = ['igx-simple-combo', 'igx-combo']; + + + + beforeEach(() => { + appTree = setupTestTree(); + }); + + it('should replace simple inline filteringOptions.filterable true with default behavior of the simple combo', async () => { + components.forEach(async component =>{ + const input = `<${component} [filteringOptions]="{ filterable: true }">`; + appTree.create(makeTemplate(`${component}-inline-true`), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate(`${component}-inline-true`)); + + expect(output).not.toContain('[disableFiltering]'); + expect(output).not.toContain('filterable'); + }); + }); + + it('should handle mixed object literal correctly', async () => { + components.forEach(async component =>{ + const input = `<${component} [filteringOptions]="{ filterable: false, caseSensitive: true }">`; + appTree.create(makeTemplate(`${component}-inline2`), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate(`${component}-inline2`)); + + expect(output).toContain(`[disableFiltering]="true"`); + expect(output).toContain(`[filteringOptions]="{ caseSensitive: true }"`); + }); + }); + + it('should warn on variable reference', async () => { + components.forEach(async component =>{ + const input = `<${component} [filteringOptions]="filterOpts"">`; + const warnMsg = "Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + + "Since it has been deprecated.'"; + + appTree.create(makeTemplate(`${component}-referenceInTsFile`), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate(`${component}-referenceInTsFile`)); + + expect(output).toContain('[filteringOptions]'); + expect(output).toContain(warnMsg); + }); + }); + + it('should skip adding new [disableFiltering] if already present on igx-combo', async () => { + const input = ``; + appTree.create(makeTemplate('combo-has-disableFiltering'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeTemplate('combo-has-disableFiltering')); + + const occurrences = (output.match(/\[disableFiltering\]/g) || []).length; + + expect(occurrences).toBe(1); + expect(output).not.toContain('filterable'); + }); + + // TS file tests + + it('should remove line when filteringOptions.filterable is set to true', async () => { + const input = `this.igxSimpleCombo.filteringOptions.filterable = true;`; + appTree.create(makeScript('tsRemoveTrue'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsRemoveTrue')); + + expect(output).not.toContain('filteringOptions.filterable'); + }); + + it('should replace filteringOptions.filterable = false with disableFiltering = true', async () => { + const input = `this.igxSimpleCombo.filteringOptions.filterable = false;`; + appTree.create(makeScript('tsReplaceFalse'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsReplaceFalse')); + + expect(output).toContain('this.igxSimpleCombo.disableFiltering = true;'); + }); + + it('should handle the use of negative flag correctly', async () => { + const input = `this.igxSimpleCombo.filteringOptions.filterable = !this.disableFilteringFlag;`; + appTree.create(makeScript('tsNegativeFlag'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsNegativeFlag')); + + expect(output).toContain('this.igxSimpleCombo.disableFiltering = this.disableFilteringFlag;'); + }); + + it('should handle the use of possitive flag correctly', async () => { + const input = `this.igxSimpleCombo.filteringOptions.filterable = this.disableFilteringFlag;`; + appTree.create(makeScript('tsNegativeFlag'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsNegativeFlag')); + + expect(output).toContain('this.igxSimpleCombo.disableFiltering = !this.disableFilteringFlag;'); + }); + + it('should split filteringOptions object and move filterable out', async () => { + const input = `this.igxSimpleCombo.filteringOptions = { filterable: false, caseSensitive: true };`; + appTree.create(makeScript('tsSplitObj'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsSplitObj')); + + expect(output).toContain('this.igxSimpleCombo.disableFiltering = true;'); + expect(output).toContain('this.igxSimpleCombo.filteringOptions = { caseSensitive: true };'); + expect(output).not.toContain('filterable'); + }); + + it('should not add disableFiltering again if already present when filterable is set to false', async () => { + const input = ` + this.igxCombo.filteringOptions.filterable = false; + this.igxCombo.disableFiltering = true; + `; + appTree.create(makeScript('tsDirectFalseExistingDisable'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsDirectFalseExistingDisable')); + + const occurrences = (output.match(/\.disableFiltering/g) || []).length; + + expect(occurrences).toBe(1); + expect(output).not.toContain('filterable'); + }); + + it('should not add disableFiltering again if already present when using negative flag assignment', async () => { + const input = ` + this.igxSimpleCombo.filteringOptions.filterable = !this.flag; + this.igxSimpleCombo.disableFiltering = this.flag; + `; + appTree.create(makeScript('tsNegativeFlagExistingDisable'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsNegativeFlagExistingDisable')); + + const occurrences = (output.match(/\.disableFiltering/g) || []).length; + + expect(occurrences).toBe(1); + expect(output).not.toContain('filterable'); + }); + + it('should not add disableFiltering again if already present when using positive flag assignment', async () => { + const input = ` + this.igxSimpleCombo.filteringOptions.filterable = this.flag; + this.igxSimpleCombo.disableFiltering = !this.flag; + `; + appTree.create(makeScript('tsPositiveFlagExistingDisable'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsPositiveFlagExistingDisable')); + + const occurrences = (output.match(/\.disableFiltering/g) || []).length; + + expect(occurrences).toBe(1); + expect(output).not.toContain('filterable'); + }); + + it('should split filteringOptions object and remove filterable if disableFiltering is already present', async () => { + const input = ` + this.igxCombo.filteringOptions = { filterable: false, caseSensitive: true }; + this.igxCombo.disableFiltering = true; + `; + appTree.create(makeScript('tsSplitObjAndDisabledFiltering'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsSplitObjAndDisabledFiltering')); + + const occurrences = (output.match(/\.disableFiltering/g) || []).length; + + expect(occurrences).toBe(1); + expect(output).toContain('this.igxCombo.filteringOptions = { caseSensitive: true };'); + expect(output).not.toContain('filterable'); + }); + + it('should insert warning comment when filteringOptions is assigned from a variable', async () => { + const input = `this.igxSimpleCombo.filteringOptions = filterOpts;`; + const expectedComment = "// Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + + "Since it has been deprecated.'"; + + appTree.create(makeScript('tsVariableAssign'), input); + + const tree = await runner.runSchematic(migrationName, {}, appTree); + const output = tree.readContent(makeScript('tsVariableAssign')); + + expect(output).toContain(input); + expect(output).toContain(expectedComment); + }); +}); diff --git a/projects/igniteui-angular/migrations/update-20_0_6/index.ts b/projects/igniteui-angular/migrations/update-20_0_6/index.ts new file mode 100644 index 00000000000..5df797fedd2 --- /dev/null +++ b/projects/igniteui-angular/migrations/update-20_0_6/index.ts @@ -0,0 +1,202 @@ +import { Element } from '@angular/compiler'; +import type { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { UpdateChanges } from '../common/UpdateChanges'; +import { + FileChange, + findElementNodes, + getSourceOffset, + parseFile +} from '../common/util'; +import { nativeImport } from 'igniteui-angular/migrations/common/import-helper.js'; + +const version = '20.0.6'; + +export default (): Rule => async (host: Tree, context: SchematicContext) => { + context.logger.info( + `Applying migration for Ignite UI for Angular to version ${version}` + ); + + const { HtmlParser } = await nativeImport('@angular/compiler') as typeof import('@angular/compiler'); + + const update = new UpdateChanges(__dirname, host, context); + const changes = new Map(); + const parser = new HtmlParser(); + + const warnMsg = "Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + + "Since it has been deprecated.'"; + + const applyChanges = () => { + for (const [path, fileChanges] of changes.entries()) { + let content = host.read(path).toString(); + fileChanges.sort((a, b) => b.position - a.position).forEach(c => { + content = c.apply(content); + }); + host.overwrite(path, content); + } + }; + + const addChange = (path: string, change: FileChange) => { + if (!changes.has(path)) { + changes.set(path, []); + } + changes.get(path).push(change); + }; + + const COMBO_TAGS = ['igx-simple-combo', 'igx-combo']; + + for (const path of update.templateFiles) { + const nodes = findElementNodes(parseFile(parser, host, path), COMBO_TAGS); + + for (const node of nodes) { + if (!(node instanceof Element)) continue; + + const hasDisableFiltering = node.attrs.some(a => a.name.includes('disableFiltering')); + const attr = node.attrs.find(a => a.name === '[filteringOptions]'); + if (!attr) continue; + + const attrVal = attr.value.trim(); + const offset = getSourceOffset(node); + const file = offset.file; + + let replacementText = ''; + + if (attrVal.startsWith('{')) { + // inline object literal + const normalized = attrVal + .replace(/'/g, '"') + .replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":'); + const parsed = JSON.parse(normalized); + const filterable = parsed.filterable; + + if (filterable === false && !hasDisableFiltering) { + replacementText += `[disableFiltering]="true"`; + } + + const remaining = { ...parsed }; + delete remaining.filterable; + const remainingProps = Object.entries(remaining) + .map(([k, v]) => `${k}: ${JSON.stringify(v)}`) + .join(', '); + + if (remainingProps.length > 0) { + replacementText += ` [filteringOptions]="{ ${remainingProps} }"`; + } + + // Replace whole [filteringOptions] attribute + const match = node.sourceSpan.toString().match(/\[filteringOptions\]="([^"]+)"/); + if (match) { + const attrText = match[0]; + const attrPos = file.content.indexOf(attrText, offset.startTag.start); + addChange(file.url, new FileChange(attrPos, replacementText, attrText, 'replace')); + } + } else { + // log for manual TS edit + const comment = `\n\n`; + addChange(file.url, new FileChange(offset.startTag.end, comment)); + } + } + } + + applyChanges(); + + for (const path of update.tsFiles) { + const content = host.read(path).toString(); + const lines = content.split('\n'); + const hasDisableFiltering = lines.some(l => /\.disableFiltering\s*=/.test(l)); + const newLines: string[] = []; + + let modified = false; + + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; + + const directTrue = /\.filteringOptions\.filterable\s*=\s*true\s*;/.test(line); + const directFalse = /\.filteringOptions\.filterable\s*=\s*false\s*;/.test(line); + const negativeFlagBinding = /\.filteringOptions\.filterable\s*=\s*!\s*(.+);/.exec(line); + const positiveFlagBinding = /\.filteringOptions\.filterable\s*=\s*(this\.\w+);/.exec(line); + const inlineObjectAssign = /\.filteringOptions\s*=\s*{[^}]*}/.test(line); + const filterableInObject = /filterable\s*:\s*([^,}]+)/.exec(line); + const objectAssignment = /\.filteringOptions\s*=\s*[^;{]+;/.test(line); + + if (directTrue) { + // Remove the line entirely + modified = true; + continue; + } + + if (directFalse) { + if (hasDisableFiltering) { + modified = true; + continue; + } + + line = line.replace(/\.filteringOptions\.filterable\s*=\s*false\s*;/, '.disableFiltering = true;'); + modified = true; + } else if (negativeFlagBinding) { + if (hasDisableFiltering) { + modified = true; + continue; + } + + const variable = negativeFlagBinding[1].trim(); + line = line.replace(/\.filteringOptions\.filterable\s*=\s*!\s*.+;/, `.disableFiltering = ${variable};`); + modified = true; + } else if (positiveFlagBinding) { + if (hasDisableFiltering) { + modified = true; + continue; + } + + const variable = positiveFlagBinding[1].trim(); + line = line.replace(/\.filteringOptions\.filterable\s*=\s*(this\.\w+);/, `.disableFiltering = !${variable};`); + modified = true; + }else if (inlineObjectAssign && filterableInObject) { + if(hasDisableFiltering){ + // clear object literal without adding disableFiltering + const cleanedLine = line + .replace(/filterable\s*:\s*[^,}]+,?\s*/g, '') + .replace(/,\s*}/, ' }') + .replace(/{\s*}/, '{}'); + + newLines.push(cleanedLine); + modified = true; + continue; + } + + const filterVal = filterableInObject[1].trim(); + + let disableFilteringValue: string; + if (filterVal === 'true') { + disableFilteringValue = 'false'; + } else if (filterVal === 'false') { + disableFilteringValue = 'true'; + } else if (filterVal.startsWith('!')) { + disableFilteringValue = filterVal.slice(1).trim(); + } else { + disableFilteringValue = `!${filterVal}`; + } + + const cleanedLine = line + .replace(/filterable\s*:\s*[^,}]+,?\s*/g, '') + .replace(/,\s*}/, ' }') + .replace(/{\s*}/, '{}'); + + // Output both lines + newLines.push(line.replace(/\.filteringOptions\s*=.*/, `.disableFiltering = ${disableFilteringValue};`)); + newLines.push(cleanedLine); + modified = true; + continue; + } else if (objectAssignment) { + newLines.push('// '+ warnMsg); + modified = true; + } + newLines.push(line); + } + + if (modified) { + host.overwrite(path, newLines.join('\n')); + } + } + + update.applyChanges(); +}; diff --git a/projects/igniteui-angular/src/lib/combo/combo.common.ts b/projects/igniteui-angular/src/lib/combo/combo.common.ts index 45d877cc4a6..2f23b19304c 100644 --- a/projects/igniteui-angular/src/lib/combo/combo.common.ts +++ b/projects/igniteui-angular/src/lib/combo/combo.common.ts @@ -95,11 +95,6 @@ export const enum DataTypes { export interface IComboFilteringOptions { /** Defines filtering case-sensitivity */ caseSensitive?: boolean; - /** - * Defines whether filtering is allowed - * @deprecated in version 18.2.0. Use the `disableFiltering` property instead. - */ - filterable?: boolean; /** Defines optional key to filter against complex list items. Default to displayKey if provided.*/ filteringKey?: string; } From 17693fab9b4ed173b56f17a7b07f98c2f5b441c3 Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Wed, 9 Jul 2025 14:56:22 +0300 Subject: [PATCH 6/7] chore(combo): Simplyfing migration to only provide comment --- CHANGELOG.md | 2 +- .../migrations/migration-collection.json | 2 +- .../migrations/update-20_0_6/index.spec.ts | 129 ++---------------- .../migrations/update-20_0_6/index.ts | 88 +----------- 4 files changed, 21 insertions(+), 200 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7aeb86f9c3..f4a930d537e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes for each version of this project will be documented in this file. -## 20.0.5 +## 20.0.6 ### General - `IgxSimpleCombo` - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. diff --git a/projects/igniteui-angular/migrations/migration-collection.json b/projects/igniteui-angular/migrations/migration-collection.json index 0d4a1970d3a..fff8a4ac96e 100644 --- a/projects/igniteui-angular/migrations/migration-collection.json +++ b/projects/igniteui-angular/migrations/migration-collection.json @@ -239,7 +239,7 @@ }, "migration-48": { "version": "20.0.6", - "description": "Updates Ignite UI for Angular from v20.0.2 to v20.0.5", + "description": "Updates Ignite UI for Angular from v20.0.2 to v20.0.6", "factory": "./update-20_0_6" } } diff --git a/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts b/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts index 71d960d15cd..31c15ad8b0b 100644 --- a/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts +++ b/projects/igniteui-angular/migrations/update-20_0_6/index.spec.ts @@ -76,134 +76,31 @@ describe('Migration 20.0.6 - Replace filteringOptions.filterable', () => { // TS file tests - it('should remove line when filteringOptions.filterable is set to true', async () => { - const input = `this.igxSimpleCombo.filteringOptions.filterable = true;`; - appTree.create(makeScript('tsRemoveTrue'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsRemoveTrue')); - - expect(output).not.toContain('filteringOptions.filterable'); - }); - - it('should replace filteringOptions.filterable = false with disableFiltering = true', async () => { - const input = `this.igxSimpleCombo.filteringOptions.filterable = false;`; - appTree.create(makeScript('tsReplaceFalse'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsReplaceFalse')); - - expect(output).toContain('this.igxSimpleCombo.disableFiltering = true;'); - }); - - it('should handle the use of negative flag correctly', async () => { - const input = `this.igxSimpleCombo.filteringOptions.filterable = !this.disableFilteringFlag;`; - appTree.create(makeScript('tsNegativeFlag'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsNegativeFlag')); - - expect(output).toContain('this.igxSimpleCombo.disableFiltering = this.disableFilteringFlag;'); - }); - - it('should handle the use of possitive flag correctly', async () => { - const input = `this.igxSimpleCombo.filteringOptions.filterable = this.disableFilteringFlag;`; - appTree.create(makeScript('tsNegativeFlag'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsNegativeFlag')); - - expect(output).toContain('this.igxSimpleCombo.disableFiltering = !this.disableFilteringFlag;'); - }); - - it('should split filteringOptions object and move filterable out', async () => { - const input = `this.igxSimpleCombo.filteringOptions = { filterable: false, caseSensitive: true };`; - appTree.create(makeScript('tsSplitObj'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsSplitObj')); - - expect(output).toContain('this.igxSimpleCombo.disableFiltering = true;'); - expect(output).toContain('this.igxSimpleCombo.filteringOptions = { caseSensitive: true };'); - expect(output).not.toContain('filterable'); - }); - - it('should not add disableFiltering again if already present when filterable is set to false', async () => { - const input = ` - this.igxCombo.filteringOptions.filterable = false; - this.igxCombo.disableFiltering = true; - `; - appTree.create(makeScript('tsDirectFalseExistingDisable'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsDirectFalseExistingDisable')); - - const occurrences = (output.match(/\.disableFiltering/g) || []).length; - - expect(occurrences).toBe(1); - expect(output).not.toContain('filterable'); - }); - - it('should not add disableFiltering again if already present when using negative flag assignment', async () => { - const input = ` - this.igxSimpleCombo.filteringOptions.filterable = !this.flag; - this.igxSimpleCombo.disableFiltering = this.flag; - `; - appTree.create(makeScript('tsNegativeFlagExistingDisable'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsNegativeFlagExistingDisable')); - - const occurrences = (output.match(/\.disableFiltering/g) || []).length; - - expect(occurrences).toBe(1); - expect(output).not.toContain('filterable'); - }); - - it('should not add disableFiltering again if already present when using positive flag assignment', async () => { - const input = ` - this.igxSimpleCombo.filteringOptions.filterable = this.flag; - this.igxSimpleCombo.disableFiltering = !this.flag; - `; - appTree.create(makeScript('tsPositiveFlagExistingDisable'), input); - - const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsPositiveFlagExistingDisable')); - - const occurrences = (output.match(/\.disableFiltering/g) || []).length; - - expect(occurrences).toBe(1); - expect(output).not.toContain('filterable'); - }); + it('should insert warning comment before `.filteringOptions.filterable = ...` assignment', async () => { + const input = `this.igxCombo.filteringOptions.filterable = false;`; + const expectedComment = "// Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + + "Since it has been deprecated.'"; - it('should split filteringOptions object and remove filterable if disableFiltering is already present', async () => { - const input = ` - this.igxCombo.filteringOptions = { filterable: false, caseSensitive: true }; - this.igxCombo.disableFiltering = true; - `; - appTree.create(makeScript('tsSplitObjAndDisabledFiltering'), input); + appTree.create(makeScript('tsWarnOnDirectAssignment'), input); const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsSplitObjAndDisabledFiltering')); - - const occurrences = (output.match(/\.disableFiltering/g) || []).length; + const output = tree.readContent(makeScript('tsWarnOnDirectAssignment')); - expect(occurrences).toBe(1); - expect(output).toContain('this.igxCombo.filteringOptions = { caseSensitive: true };'); - expect(output).not.toContain('filterable'); + expect(output).toContain(expectedComment); + expect(output).toContain('this.igxCombo.filteringOptions.filterable = false;'); }); - it('should insert warning comment when filteringOptions is assigned from a variable', async () => { - const input = `this.igxSimpleCombo.filteringOptions = filterOpts;`; + it('should insert warning comment before `.filteringOptions = { ... }` assignment', async () => { + const input = `this.igxCombo.filteringOptions = { filterable: false, caseSensitive: true };`; const expectedComment = "// Manual migration needed: please use 'disableFiltering' instead of filteringOptions.filterable." + "Since it has been deprecated.'"; - appTree.create(makeScript('tsVariableAssign'), input); + appTree.create(makeScript('tsWarnOnObjectAssignment'), input); const tree = await runner.runSchematic(migrationName, {}, appTree); - const output = tree.readContent(makeScript('tsVariableAssign')); + const output = tree.readContent(makeScript('tsWarnOnObjectAssignment')); - expect(output).toContain(input); expect(output).toContain(expectedComment); + expect(output).toContain('this.igxCombo.filteringOptions = { filterable: false, caseSensitive: true };'); }); }); diff --git a/projects/igniteui-angular/migrations/update-20_0_6/index.ts b/projects/igniteui-angular/migrations/update-20_0_6/index.ts index 5df797fedd2..c4f110500eb 100644 --- a/projects/igniteui-angular/migrations/update-20_0_6/index.ts +++ b/projects/igniteui-angular/migrations/update-20_0_6/index.ts @@ -102,92 +102,16 @@ export default (): Rule => async (host: Tree, context: SchematicContext) => { for (const path of update.tsFiles) { const content = host.read(path).toString(); const lines = content.split('\n'); - const hasDisableFiltering = lines.some(l => /\.disableFiltering\s*=/.test(l)); const newLines: string[] = []; let modified = false; - for (let i = 0; i < lines.length; i++) { - let line = lines[i]; - - const directTrue = /\.filteringOptions\.filterable\s*=\s*true\s*;/.test(line); - const directFalse = /\.filteringOptions\.filterable\s*=\s*false\s*;/.test(line); - const negativeFlagBinding = /\.filteringOptions\.filterable\s*=\s*!\s*(.+);/.exec(line); - const positiveFlagBinding = /\.filteringOptions\.filterable\s*=\s*(this\.\w+);/.exec(line); - const inlineObjectAssign = /\.filteringOptions\s*=\s*{[^}]*}/.test(line); - const filterableInObject = /filterable\s*:\s*([^,}]+)/.exec(line); - const objectAssignment = /\.filteringOptions\s*=\s*[^;{]+;/.test(line); - - if (directTrue) { - // Remove the line entirely - modified = true; - continue; - } - - if (directFalse) { - if (hasDisableFiltering) { - modified = true; - continue; - } - - line = line.replace(/\.filteringOptions\.filterable\s*=\s*false\s*;/, '.disableFiltering = true;'); - modified = true; - } else if (negativeFlagBinding) { - if (hasDisableFiltering) { - modified = true; - continue; - } - - const variable = negativeFlagBinding[1].trim(); - line = line.replace(/\.filteringOptions\.filterable\s*=\s*!\s*.+;/, `.disableFiltering = ${variable};`); - modified = true; - } else if (positiveFlagBinding) { - if (hasDisableFiltering) { - modified = true; - continue; - } - - const variable = positiveFlagBinding[1].trim(); - line = line.replace(/\.filteringOptions\.filterable\s*=\s*(this\.\w+);/, `.disableFiltering = !${variable};`); - modified = true; - }else if (inlineObjectAssign && filterableInObject) { - if(hasDisableFiltering){ - // clear object literal without adding disableFiltering - const cleanedLine = line - .replace(/filterable\s*:\s*[^,}]+,?\s*/g, '') - .replace(/,\s*}/, ' }') - .replace(/{\s*}/, '{}'); - - newLines.push(cleanedLine); - modified = true; - continue; - } - - const filterVal = filterableInObject[1].trim(); - - let disableFilteringValue: string; - if (filterVal === 'true') { - disableFilteringValue = 'false'; - } else if (filterVal === 'false') { - disableFilteringValue = 'true'; - } else if (filterVal.startsWith('!')) { - disableFilteringValue = filterVal.slice(1).trim(); - } else { - disableFilteringValue = `!${filterVal}`; - } - - const cleanedLine = line - .replace(/filterable\s*:\s*[^,}]+,?\s*/g, '') - .replace(/,\s*}/, ' }') - .replace(/{\s*}/, '{}'); - - // Output both lines - newLines.push(line.replace(/\.filteringOptions\s*=.*/, `.disableFiltering = ${disableFilteringValue};`)); - newLines.push(cleanedLine); - modified = true; - continue; - } else if (objectAssignment) { - newLines.push('// '+ warnMsg); + for (const line of lines) { + if ( + /\.filteringOptions\.filterable\s*=/.test(line) || + /\.filteringOptions\s*=/.test(line) + ) { + newLines.push('// ' + warnMsg); modified = true; } newLines.push(line); From 386a3e420d1ac1c121f025801e872fa72f92e296 Mon Sep 17 00:00:00 2001 From: Ivan Kitanov Date: Wed, 9 Jul 2025 16:20:54 +0300 Subject: [PATCH 7/7] chore(combo): Removing unnecessary code --- CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4a930d537e..2b00af074ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,13 +48,6 @@ All notable changes for each version of this project will be documented in this ``` -## 19.2.14 -### General -- `IgxSimpleCombo` - - Added `disableFiltering` to the `IgxSimpleCombo`, which enables/disables the filtering in the list. The default is `false`. With this introduction, `filteringOptions.filterable` is no longer usde. -- `IgxCombo`, `IgxSimpleCombo` - - Removed deprecated `filteringOptions.filterable` option. - ## 19.2.0 ### General - `IgxCarousel`