From 9cc687463aaefcca18eab2eb1ffb4998ea67a9e0 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 24 Jul 2024 19:44:01 +0300 Subject: [PATCH 001/147] feat(query-builder): initial support for entities and return fields --- .../data-operations/filtering-condition.ts | 6 +- .../filtering-expressions-tree.ts | 12 +- .../src/lib/grids/common/grid.interface.ts | 8 + .../query-builder.component.html | 145 +++++++++++------- .../query-builder/query-builder.component.ts | 138 ++++++++++++++++- .../query-builder/query-builder.sample.html | 2 +- src/app/query-builder/query-builder.sample.ts | 83 +++++----- 7 files changed, 291 insertions(+), 103 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts index b2db8b4500f..ae8dbae8f95 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts @@ -20,10 +20,10 @@ export class IgxFilteringOperand { iconName: 'is-not-null', logic: (target: any) => target !== null }, { - name: 'in', + name: 'IN', isUnary: false, - iconName: 'is-in', - hidden: true, + iconName: 'starts-with', + // hidden: true, logic: (target: any, searchVal: Set) => this.findValueInSet(target, searchVal) }]; } diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts index 92b84be6a5e..80a8496639a 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts @@ -11,6 +11,8 @@ export declare interface IExpressionTree { filteringOperands: (IExpressionTree | IFilteringExpression)[]; operator: FilteringLogic; fieldName?: string; + entity?: string; + returnFields?: string | string[]; } /* marshalByValue */ @@ -89,9 +91,15 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { */ public type?: FilteringExpressionsTreeType; - constructor(operator: FilteringLogic, fieldName?: string) { + public entity?: string; + + public returnFields?: string | string[]; + + constructor(operator: FilteringLogic, entity?: string, returnFields?: string | string[]) { this.operator = operator; - this.fieldName = fieldName; + this.entity = entity; + this.returnFields = returnFields; + // this.fieldName = fieldName; } diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index d9419371826..aa25b3f7ce2 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -1480,3 +1480,11 @@ export interface IClipboardOptions { */ separator: string; } + +/** + * An interface describing entity + */ +export interface EntityType { + name: string; + fields: FieldType[]; +} diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 55f2f7a2728..0829d31362c 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -97,10 +97,9 @@
(remove)="onChipRemove(expressionItem)" (selectedChanged)="onChipSelectionEnd()" > - {{ - expressionItem.fieldLabel || - expressionItem.expression.fieldName - }} + + {{this.expressionTree.entity}} / {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} + this.resourceStrings.igx_query_builder_column_placeholder " [(ngModel)]="selectedField" + [disabled]="!fields" > {{ field.label || field.header || field.field }} @@ -259,7 +259,8 @@
!selectedCondition || (selectedField && selectedField.filters.condition(selectedCondition) - .isUnary) + .isUnary) || + selectedCondition.toLowerCase() === 'in' " [type]=" selectedField && selectedField.dataType === 'number' @@ -370,6 +371,8 @@
+ +
- -
-
+ +
+ + + + + {{entity.name}} + + + -
- - - - + + / + + + * + + {{ field.label || field.header || field.field }} + + + + +
- + tabindex="0" + class="igx-filter-tree__line" + [ngClass]="{ + 'igx-filter-tree__line--and': expressionItem.operator === 0, + 'igx-filter-tree__line--or': expressionItem.operator === 1, + 'igx-filter-tree__line--selected': expressionItem.selected + }" + (keydown)="invokeClick($event)" + (click)="onGroupClick(expressionItem)" + >
+ +
+ + + - + + + +
@@ -522,6 +562,7 @@
+
{ + // this.setFilters(field); + // this.setFormat(field); + // }); + // } + } + /** * Returns the fields. */ @@ -220,6 +247,9 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { */ @Output() public expressionTreeChange = new EventEmitter(); + + // @ViewChildren('entitySelect', { read: IgxSelectComponent }) + // protected entitySelect: QueryList; @ViewChild('fieldSelect', { read: IgxSelectComponent }) private fieldSelect: IgxSelectComponent; @@ -347,6 +377,20 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public pickerOutlet: IgxOverlayOutletDirective | ElementRef; + + /** + * @hidden @internal + */ + public selectedReturnFields: string | string[]; + + // /** + // * @hidden @internal + // */ + // public entitySelectOverlaySettings: OverlaySettings = { + // scrollStrategy: new AbsoluteScrollStrategy(), + // modal: false, + // closeOnOutsideClick: false + // }; /** * @hidden @internal @@ -367,6 +411,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { }; private destroy$ = new Subject(); + private _selectedEntity: EntityType; private _selectedField: FieldType; private _clickTimer; private _dblClickDelay = 200; @@ -377,6 +422,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { private _addModeExpression: ExpressionOperandItem; private _editedExpression: ExpressionOperandItem; private _selectedGroups: ExpressionGroupItem[] = []; + private _entities: EntityType[]; private _fields: FieldType[]; private _expressionTree: IExpressionTree; private _locale; @@ -394,6 +440,13 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { scrollStrategy: new CloseScrollStrategy() }; + // private _entitiesOverlaySettings: OverlaySettings = { + // closeOnOutsideClick: true, + // modal: false, + // scrollStrategy: new AbsoluteScrollStrategy(), + // positionStrategy: new ConnectedPositioningStrategy(this._positionSettings) + // }; + constructor(public cdr: ChangeDetectorRef, protected iconService: IgxIconService, protected platform: PlatformUtil, @@ -407,6 +460,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { */ public ngAfterViewInit(): void { this._overlaySettings.outlet = this.overlayOutlet; + // this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; } @@ -419,6 +473,33 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.destroy$.complete(); } + /** + * @hidden @internal + */ + public set selectedEntity(value: EntityType) { + let oldValue = null; + if (this.expressionTree) { + oldValue = this.expressionTree.entity; + } + if (this._selectedEntity !== value) { + this._selectedEntity = value; + this.fields = value ? value.fields : null; + if (oldValue && this._selectedEntity /*&& this._selectedEntity !== oldValue*/) { + this.selectedField = null; + this.selectedCondition = null; + this.searchValue = null; + this.cdr.detectChanges(); + } + } + } + + /** + * @hidden @internal + */ + public get selectedEntity(): EntityType { + return this._selectedEntity; + } + /** * @hidden @internal */ @@ -529,6 +610,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); + this.expressionTree.entity = this.selectedEntity.name; + this.expressionTree.returnFields = this.selectedReturnFields; this.expressionTreeChange.emit(); } @@ -630,7 +713,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } expressionItem.hovered = false; - + if (this.expressionTree) { + this._selectedEntity = this.entities.find(entity => entity.name === this.expressionTree.entity); + this.selectedReturnFields = this.expressionTree.returnFields; + } + this.fields = this.selectedEntity ? this.selectedEntity.fields : null; this.selectedField = expressionItem.expression.fieldName ? this.fields.find(field => field.field === expressionItem.expression.fieldName) : null; this.selectedCondition = expressionItem.expression.condition ? @@ -642,6 +729,9 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.cdr.detectChanges(); + // this.entitySelectOverlaySettings.target = this.entitySelect.element; + // this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; + // this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); this.fieldSelectOverlaySettings.target = this.fieldSelect.element; this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); @@ -900,6 +990,38 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } + /** + * @hidden @internal + */ + public onExpressionTreeEntityChanged(eventArgs: ISelectionEventArgs) { + this.selectedEntity = eventArgs.newSelection.value; + this.selectedReturnFields = null; + + const entity = this.entities.find(el => el.name === this.selectedEntity.name); + this.fields = entity.fields; + + if (this.expressionTree) { + this.expressionTree.entity = this.selectedEntity.name; + this.expressionTree.returnFields = null; + + // TODO: reset conditions on entity change + } + } + + /** + * @hidden @internal + */ + public onExpressionReturnFieldChanged(eventArgs: ISelectionEventArgs) { + this.selectedReturnFields = + typeof eventArgs.newSelection.value === 'string' ? + eventArgs.newSelection.value : + eventArgs.newSelection.value.field;; + + if (this.expressionTree) { + this.expressionTree.returnFields = this.selectedReturnFields; + } + } + private setFormat(field: FieldType) { if (!field.pipeArgs) { field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; @@ -1005,6 +1127,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { ignoreCase: filteringExpr.ignoreCase }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); + const entity = this.entities.find(el => el.name === expressionTree.entity); + this.fields = entity.fields; const field = this.fields.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); @@ -1096,7 +1220,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { const children = expressionItem.parent.children; const index = children.indexOf(expressionItem); children.splice(index, 1); - this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); + const entity = this.expressionTree ? this.expressionTree.entity : null; + const returnFields = this.expressionTree ? this.expressionTree.returnFields : null; + this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); // TODO: don't recreate if not necessary + this._expressionTree.entity = entity; + this._expressionTree.returnFields = returnFields; if (!children.length) { this.deleteItem(expressionItem.parent); diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 0df8870cc71..774bb942238 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -4,7 +4,7 @@ diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 0f1aa18b314..0dd25305841 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -22,56 +22,59 @@ export class QueryBuilderComponent implements OnInit { @ViewChild('queryBuilder', { static: true }) public queryBuilder: IgxQueryBuilderComponent; + public entities: Array; public fields: Array; public displayDensities; public expressionTree: IExpressionTree; public ngOnInit(): void { - this.fields = [ - { field: 'ID', dataType: 'string' }, - { field: 'CompanyName', dataType: 'string'}, - { field: 'ContactName', dataType: 'string' }, - { field: 'Employees', dataType: 'number' }, - { field: 'ContactTitle', dataType: 'string' }, - { field: 'DateCreated', dataType: 'date' }, - { field: 'TimeCreated', dataType: 'time' }, - { field: 'Address', dataType: 'string' }, - { field: 'City', dataType: 'string' }, - { field: 'Region', dataType: 'string' }, - { field: 'PostalCode', dataType: 'string' }, - { field: 'Phone', dataType: 'string' }, - { field: 'Fax', dataType: 'string' }, - { field: 'Contract', dataType: 'boolean' } + this.entities = [ + { + name: 'Assays', fields: [ + { field: 'Id', dataType: 'number' }, + { field: 'CompoundId', dataType: 'number' }, + { field: 'Name', dataType: 'string' }, + { field: 'EndpointName', dataType: 'string' }, + { field: 'EndpointValue', dataType: 'string' } + ] + }, + { + name: 'Compounds', fields: [ + { field: 'Id', dataType: 'number' }, + { field: 'Structure', dataType: 'string' } + ] + } ]; - const tree = new FilteringExpressionsTree(FilteringLogic.And); + const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', '*'); tree.filteringOperands.push({ - fieldName: 'ID', - condition: IgxStringFilteringOperand.instance().condition('contains'), - searchVal: 'a', - ignoreCase: true - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ID', - condition: IgxStringFilteringOperand.instance().condition('contains'), - searchVal: 'b', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'CompanyName', - condition: IgxStringFilteringOperand.instance().condition('contains'), - searchVal: 'c', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - tree.filteringOperands.push({ - fieldName: 'CompanyName', - condition: IgxStringFilteringOperand.instance().condition('contains'), - searchVal: 'd', - ignoreCase: true + fieldName: 'Name', + condition: IgxStringFilteringOperand.instance().condition('equals'), + searchVal: 'Hepacity', + // ignoreCase: true }); + // const orTree = new FilteringExpressionsTree(FilteringLogic.Or); + // orTree.filteringOperands.push({ + // fieldName: 'ID', + // condition: IgxStringFilteringOperand.instance().condition('contains'), + // searchVal: 'b', + // ignoreCase: true + // }); + // orTree.filteringOperands.push({ + // fieldName: 'CompanyName', + // condition: IgxStringFilteringOperand.instance().condition('contains'), + // searchVal: 'c', + // ignoreCase: true + // }); + // tree.filteringOperands.push(orTree); + // tree.filteringOperands.push({ + // fieldName: 'CompanyName', + // condition: IgxStringFilteringOperand.instance().condition('contains'), + // searchVal: 'd', + // ignoreCase: true + // }); + this.expressionTree = tree; } From 8f75799efb4464a12eee23eb66828917785af5e8 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 31 Jul 2024 16:19:50 +0300 Subject: [PATCH 002/147] feat(query-builder): selects for entity and return fields --- .../filtering-expression.interface.ts | 3 +- .../query-builder.component.html | 144 ++++++++++-------- .../query-builder/query-builder.component.ts | 123 ++++++++------- src/app/query-builder/query-builder.sample.ts | 21 ++- 4 files changed, 169 insertions(+), 122 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index 482ee7ea77e..2218fdd64e1 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -14,6 +14,7 @@ export enum FilteringLogic { export declare interface IFilteringExpression { fieldName: string; condition: IFilteringOperation; - searchVal?: any; + searchVal?: any; + searchTree?: any; ignoreCase?: boolean; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 0829d31362c..7de2fe13806 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -44,11 +44,7 @@
- + - +
*ngIf="!expressionItem.expression.condition.isUnary" > {{ - isDate(expressionItem.expression.searchVal) - ? getFormatter(expressionItem.expression.fieldName) - ? (expressionItem.expression.searchVal - | fieldFormatter - : getFormatter( - expressionItem.expression.fieldName - ) - : undefined) - : (expressionItem.expression.searchVal - | date - : getFormat( - expressionItem.expression.fieldName - ) - : undefined - : this.locale) - : expressionItem.expression.searchVal + expressionItem.expression.searchTree + ? (expressionItem.expression.searchTree.entity + ' / ' + expressionItem.expression.searchTree.returnFields) + : isDate(expressionItem.expression.searchVal) + ? getFormatter(expressionItem.expression.fieldName) + ? (expressionItem.expression.searchVal + | fieldFormatter + : getFormatter( + expressionItem.expression.fieldName + ) + : undefined) + : (expressionItem.expression.searchVal + | date + : getFormat( + expressionItem.expression.fieldName + ) + : undefined + : this.locale) + : expressionItem.expression.searchVal }} @@ -171,6 +169,22 @@
>
+ +
+ + + + + + +
[(ngModel)]="searchValue" /> + +
+ +
-
- -
- - - - - {{entity.name}} - - - - - - / - - - * - - {{ field.label || field.header || field.field }} - - - - + +
- +
+ + + {{entity.name}} + + + + + + {{ field.label || field.header || field.field }} + + +
+ + @@ -492,7 +502,7 @@
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index ef2a7dec347..f1240cb8361 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -37,9 +37,6 @@ import { IgxPrefixDirective } from '../directives/prefix/prefix.directive'; import { IgxIconComponent } from '../icon/icon.component'; import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; -import { IgxDropDownItemNavigationDirective } from '../drop-down/drop-down-navigation.directive'; -import { IgxDropDownComponent } from "../drop-down/drop-down.component"; -import { IgxDropDownItemComponent } from "../drop-down/drop-down-item.component"; import { ISelectionEventArgs } from '../drop-down/drop-down.common'; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; @@ -120,7 +117,7 @@ class ExpressionOperandItem extends ExpressionItem { selector: 'igx-query-builder', templateUrl: './query-builder.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxDropDownItemNavigationDirective, IgxDropDownComponent, IgxDropDownItemComponent] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective] }) export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { /** @@ -248,8 +245,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { @Output() public expressionTreeChange = new EventEmitter(); - // @ViewChildren('entitySelect', { read: IgxSelectComponent }) - // protected entitySelect: QueryList; + @ViewChildren('entitySelect', { read: IgxSelectComponent }) + protected entitySelect: IgxSelectComponent; + + @ViewChild('returnFieldsSelect', { read: IgxSelectComponent }) + protected returnFieldsSelect: IgxSelectComponent; @ViewChild('fieldSelect', { read: IgxSelectComponent }) private fieldSelect: IgxSelectComponent; @@ -383,14 +383,23 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { */ public selectedReturnFields: string | string[]; - // /** - // * @hidden @internal - // */ - // public entitySelectOverlaySettings: OverlaySettings = { - // scrollStrategy: new AbsoluteScrollStrategy(), - // modal: false, - // closeOnOutsideClick: false - // }; + /** + * @hidden @internal + */ + public entitySelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: false + }; + + /** + * @hidden @internal + */ + public returnFieldsSelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: false + }; /** * @hidden @internal @@ -440,13 +449,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { scrollStrategy: new CloseScrollStrategy() }; - // private _entitiesOverlaySettings: OverlaySettings = { - // closeOnOutsideClick: true, - // modal: false, - // scrollStrategy: new AbsoluteScrollStrategy(), - // positionStrategy: new ConnectedPositioningStrategy(this._positionSettings) - // }; - constructor(public cdr: ChangeDetectorRef, protected iconService: IgxIconService, protected platform: PlatformUtil, @@ -460,7 +462,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { */ public ngAfterViewInit(): void { this._overlaySettings.outlet = this.overlayOutlet; - // this.entitySelectOverlaySettings.outlet = this.overlayOutlet; + this.entitySelectOverlaySettings.outlet = this.overlayOutlet; + this.returnFieldsSelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; } @@ -483,6 +486,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } if (this._selectedEntity !== value) { this._selectedEntity = value; + this.selectedReturnFields = null; this.fields = value ? value.fields : null; if (oldValue && this._selectedEntity /*&& this._selectedEntity !== oldValue*/) { this.selectedField = null; @@ -491,6 +495,18 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.cdr.detectChanges(); } } + + this.selectedReturnFields = null; + + const entity = this.entities.find(el => el.name === this.selectedEntity.name); + this.fields = entity.fields; + + if (this.expressionTree) { + this.expressionTree.entity = this.selectedEntity.name; + this.expressionTree.returnFields = null; + + // TODO: reset conditions on entity change + } } /** @@ -729,9 +745,12 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.cdr.detectChanges(); - // this.entitySelectOverlaySettings.target = this.entitySelect.element; - // this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; - // this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + this.entitySelectOverlaySettings.target = this.entitySelect.element; + this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; + this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + this.returnFieldsSelectOverlaySettings.target = this.returnFieldsSelect.element; + this.returnFieldsSelectOverlaySettings.excludeFromOutsideClick = [this.returnFieldsSelect.element as HTMLElement]; + this.returnFieldsSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); this.fieldSelectOverlaySettings.target = this.fieldSelect.element; this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); @@ -990,37 +1009,37 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } - /** - * @hidden @internal - */ - public onExpressionTreeEntityChanged(eventArgs: ISelectionEventArgs) { - this.selectedEntity = eventArgs.newSelection.value; - this.selectedReturnFields = null; + // /** + // * @hidden @internal + // */ + // public onExpressionTreeEntityChanged(eventArgs: ISelectionEventArgs) { + // this.selectedEntity = eventArgs.newSelection.value; + // this.selectedReturnFields = null; - const entity = this.entities.find(el => el.name === this.selectedEntity.name); - this.fields = entity.fields; + // const entity = this.entities.find(el => el.name === this.selectedEntity.name); + // this.fields = entity.fields; - if (this.expressionTree) { - this.expressionTree.entity = this.selectedEntity.name; - this.expressionTree.returnFields = null; + // if (this.expressionTree) { + // this.expressionTree.entity = this.selectedEntity.name; + // this.expressionTree.returnFields = null; - // TODO: reset conditions on entity change - } - } + // // TODO: reset conditions on entity change + // } + // } - /** - * @hidden @internal - */ - public onExpressionReturnFieldChanged(eventArgs: ISelectionEventArgs) { - this.selectedReturnFields = - typeof eventArgs.newSelection.value === 'string' ? - eventArgs.newSelection.value : - eventArgs.newSelection.value.field;; + // /** + // * @hidden @internal + // */ + // public onExpressionReturnFieldChanged(eventArgs: ISelectionEventArgs) { + // this.selectedReturnFields = + // typeof eventArgs.newSelection.value === 'string' ? + // eventArgs.newSelection.value : + // eventArgs.newSelection.value.field;; - if (this.expressionTree) { - this.expressionTree.returnFields = this.selectedReturnFields; - } - } + // if (this.expressionTree) { + // this.expressionTree.returnFields = this.selectedReturnFields; + // } + // } private setFormat(field: FieldType) { if (!field.pipeArgs) { @@ -1124,6 +1143,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { fieldName: filteringExpr.fieldName, condition: filteringExpr.condition, searchVal: filteringExpr.searchVal, + searchTree: this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), ignoreCase: filteringExpr.ignoreCase }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); @@ -1135,7 +1155,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } } - return groupItem; } diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 0dd25305841..567e133219d 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -46,13 +46,30 @@ export class QueryBuilderComponent implements OnInit { } ]; - const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', '*'); - tree.filteringOperands.push({ + const innerTree = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', 'Id'); + innerTree.filteringOperands.push({ fieldName: 'Name', condition: IgxStringFilteringOperand.instance().condition('equals'), searchVal: 'Hepacity', // ignoreCase: true }); + innerTree.filteringOperands.push({ + fieldName: 'EndpointName', + condition: IgxStringFilteringOperand.instance().condition('equals'), + searchVal: 'IC60', + // ignoreCase: true + }); + const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', 'Id'); + tree.filteringOperands.push({ + fieldName: 'Id', + condition: IgxStringFilteringOperand.instance().condition('IN'), + searchTree: innerTree + }); + tree.filteringOperands.push({ + fieldName: 'Id', + condition: IgxStringFilteringOperand.instance().condition('contains'), + searchVal: 'xxx' + }); // const orTree = new FilteringExpressionsTree(FilteringLogic.Or); // orTree.filteringOperands.push({ From 5c92102d02b945262d9d6d01f33ca4a1bd0f033c Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Mon, 5 Aug 2024 11:14:53 +0300 Subject: [PATCH 003/147] feat(query-builder): use inner query builder when search tree is present --- .../query-builder/_query-builder-theme.scss | 2 +- .../data-operations/filtering-condition.ts | 8 +- .../query-builder.component.html | 188 ++++++++---------- .../query-builder/query-builder.component.ts | 110 +++++----- .../query-builder/query-builder.sample.html | 9 +- .../query-builder/query-builder.sample.scss | 9 + src/app/query-builder/query-builder.sample.ts | 12 +- 7 files changed, 168 insertions(+), 170 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 29d4b3e55d2..2881ac0584f 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -121,7 +121,7 @@ display: block; overflow: auto; min-height: pad(rem(138px), rem(164px), rem(214px)); - max-height: pad(rem(305px), rem(394px), rem(468px)); + // max-height: pad(rem(305px), rem(394px), rem(468px)); [igxButton] + [igxButton] { margin-inline-start: rem(8px); diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts index ae8dbae8f95..2603c47a876 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts @@ -20,11 +20,17 @@ export class IgxFilteringOperand { iconName: 'is-not-null', logic: (target: any) => target !== null }, { - name: 'IN', + name: 'in', isUnary: false, iconName: 'starts-with', // hidden: true, logic: (target: any, searchVal: Set) => this.findValueInSet(target, searchVal) + }, { + name: 'notIn', + isUnary: false, + iconName: 'starts-with', + // hidden: true, + logic: (target: any, searchVal: Set) => !this.findValueInSet(target, searchVal) }]; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 7de2fe13806..4baee0569c9 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -6,13 +6,13 @@ - - + -->
>
- -
- - - - - - -
(selectedField && selectedField.filters.condition(selectedCondition) .isUnary) || - selectedCondition.toLowerCase() === 'in' + selectedCondition === 'in' " [type]=" selectedField && selectedField.dataType === 'number' @@ -287,10 +271,6 @@
[(ngModel)]="searchValue" /> - -
- -
-
+ + + + +
-
-
-
+
+
-
-
- - - {{entity.name}} - - - - - - {{ field.label || field.header || field.field }} - - -
+
+
+ + + {{entity.name}} + + - - - + + All + + {{ field.label || field.header || field.field }} + + +
+ + + + + +
+ -
- - - -
+ {{ + this.resourceStrings.igx_query_builder_end_group + }} +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index f1240cb8361..16716e22363 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -132,28 +132,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { @HostBinding('style.display') public display = 'block'; - /** - * Returns the entities. - */ - public get entities(): EntityType[] { - return this._entities; - } - - /** - * Sets the entities. - */ @Input() - public set entities(entities: EntityType[]) { - this._entities = entities; - - // if (this._entities) { - // this.registerSVGIcons(); - // this._entities.forEach(field => { - // this.setFilters(field); - // this.setFormat(field); - // }); - // } - } + public entities: EntityType[]; /** * Returns the fields. @@ -338,6 +318,9 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { @ViewChild('overlayOutlet', { read: IgxOverlayOutletDirective, static: true }) private overlayOutlet: IgxOverlayOutletDirective; + @ViewChild(IgxQueryBuilderComponent) + private innerQuery: IgxQueryBuilderComponent; + /** * @hidden @internal */ @@ -377,11 +360,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public pickerOutlet: IgxOverlayOutletDirective | ElementRef; - - /** - * @hidden @internal - */ - public selectedReturnFields: string | string[]; /** * @hidden @internal @@ -421,6 +399,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { private destroy$ = new Subject(); private _selectedEntity: EntityType; + private _selectedReturnFields: string | string[]; private _selectedField: FieldType; private _clickTimer; private _dblClickDelay = 200; @@ -431,7 +410,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { private _addModeExpression: ExpressionOperandItem; private _editedExpression: ExpressionOperandItem; private _selectedGroups: ExpressionGroupItem[] = []; - private _entities: EntityType[]; private _fields: FieldType[]; private _expressionTree: IExpressionTree; private _locale; @@ -496,8 +474,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } - this.selectedReturnFields = null; - const entity = this.entities.find(el => el.name === this.selectedEntity.name); this.fields = entity.fields; @@ -516,6 +492,28 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { return this._selectedEntity; } + /** + * @hidden @internal + */ + public set selectedReturnFields(value: string | string[]) { + const oldValue = this._selectedReturnFields; + + if (this._selectedReturnFields !== value && oldValue !== value) { + this._selectedReturnFields = value; + if (this.expressionTree) { + this.expressionTree.returnFields = value; + this.expressionTreeChange.emit(); + } + } + } + + /** + * @hidden @internal + */ + public get selectedReturnFields(): string | string[] { + return this._selectedReturnFields; + } + /** * @hidden @internal */ @@ -612,10 +610,14 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public commitOperandEdit() { + console.log('commitOperandEdit'); if (this._editedExpression) { this._editedExpression.expression.fieldName = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); + if (this.innerQuery) { + this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; + } this._editedExpression.fieldLabel = this.selectedField.label ? this.selectedField.label : this.selectedField.header @@ -660,6 +662,10 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public operandCanBeCommitted(): boolean { + return true; // TODO: enable for search tree + + console.log(this._editedExpression.fieldLabel); + console.log(this._editedExpression.expression); return this.selectedField && this.selectedCondition && (!!this.searchValue || this.selectedField.filters.condition(this.selectedCondition).isUnary); } @@ -720,6 +726,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public enterExpressionEdit(expressionItem: ExpressionOperandItem) { + console.log('enterExpressionEdit'); + // console.log(this.expressionTree); this.clearSelection(); this.exitOperandEdit(); this.cancelOperandAdd(); @@ -1009,37 +1017,12 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } - // /** - // * @hidden @internal - // */ - // public onExpressionTreeEntityChanged(eventArgs: ISelectionEventArgs) { - // this.selectedEntity = eventArgs.newSelection.value; - // this.selectedReturnFields = null; - - // const entity = this.entities.find(el => el.name === this.selectedEntity.name); - // this.fields = entity.fields; - - // if (this.expressionTree) { - // this.expressionTree.entity = this.selectedEntity.name; - // this.expressionTree.returnFields = null; - - // // TODO: reset conditions on entity change - // } - // } - - // /** - // * @hidden @internal - // */ - // public onExpressionReturnFieldChanged(eventArgs: ISelectionEventArgs) { - // this.selectedReturnFields = - // typeof eventArgs.newSelection.value === 'string' ? - // eventArgs.newSelection.value : - // eventArgs.newSelection.value.field;; - - // if (this.expressionTree) { - // this.expressionTree.returnFields = this.selectedReturnFields; - // } - // } + public onExpressionSearchTreeChange() { + console.log('onExpressionSearchTreeChange'); + console.log(this.expressionTree); + this.expressionTreeChange.emit(); + // this.cdr.detectChanges(); + } private setFormat(field: FieldType) { if (!field.pipeArgs) { @@ -1143,7 +1126,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { fieldName: filteringExpr.fieldName, condition: filteringExpr.condition, searchVal: filteringExpr.searchVal, - searchTree: this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), + searchTree: filteringExpr.searchTree, // this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), ignoreCase: filteringExpr.ignoreCase }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); @@ -1152,6 +1135,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { const field = this.fields.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); + this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); + this.selectedReturnFields = + !expressionTree.returnFields || expressionTree.returnFields === "*" || expressionTree.returnFields === "All" + ? 'All' + : this.fields.find(f => f.field === expressionTree.returnFields).field; } } } diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 774bb942238..c6a6d9001b5 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -1,17 +1,20 @@
- + -
+ + +
+
{{ printExpressionTree(queryBuilder.expressionTree) }}
diff --git a/src/app/query-builder/query-builder.sample.scss b/src/app/query-builder/query-builder.sample.scss index 90582957ea8..95306f0956a 100644 --- a/src/app/query-builder/query-builder.sample.scss +++ b/src/app/query-builder/query-builder.sample.scss @@ -8,3 +8,12 @@ display: flex; flex-wrap: wrap; } + +.output-area{ + overflow-y: auto; + height: 400px; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); + margin-top: 15px; + border-radius: 4px; + padding-left: 16px; +} \ No newline at end of file diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 567e133219d..3db2c5c4987 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -59,16 +59,16 @@ export class QueryBuilderComponent implements OnInit { searchVal: 'IC60', // ignoreCase: true }); - const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', 'Id'); + const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', '*'); tree.filteringOperands.push({ fieldName: 'Id', - condition: IgxStringFilteringOperand.instance().condition('IN'), + condition: IgxStringFilteringOperand.instance().condition('in'), searchTree: innerTree }); tree.filteringOperands.push({ - fieldName: 'Id', + fieldName: 'Structure', condition: IgxStringFilteringOperand.instance().condition('contains'), - searchVal: 'xxx' + searchVal: 'abc' }); // const orTree = new FilteringExpressionsTree(FilteringLogic.Or); @@ -101,4 +101,8 @@ export class QueryBuilderComponent implements OnInit { } this.queryBuilder.locale = locale; } + + public printExpressionTree(tree: IExpressionTree) { + return tree ? JSON.stringify(tree, null, 2) : 'Please add an expression!'; + } } From 2a0b20824200ce2fe76d871b2ea49cc5e5022a51 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Mon, 5 Aug 2024 16:29:24 +0300 Subject: [PATCH 004/147] feat(query-builder): modified return fields selection to use combo --- .../src/i18n/BG/query-builder-resources.ts | 2 ++ .../src/i18n/CS/query-builder-resources.ts | 2 ++ .../src/i18n/DA/query-builder-resources.ts | 2 ++ .../src/i18n/DE/query-builder-resources.ts | 2 ++ .../src/i18n/ES/query-builder-resources.ts | 2 ++ .../src/i18n/FR/query-builder-resources.ts | 2 ++ .../src/i18n/HU/query-builder-resources.ts | 2 ++ .../src/i18n/IT/query-builder-resources.ts | 2 ++ .../src/i18n/JA/query-builder-resources.ts | 2 ++ .../src/i18n/KO/query-builder-resources.ts | 2 ++ .../src/i18n/NB/query-builder-resources.ts | 2 ++ .../src/i18n/NL/query-builder-resources.ts | 2 ++ .../src/i18n/PL/query-builder-resources.ts | 2 ++ .../src/i18n/PT/query-builder-resources.ts | 2 ++ .../src/i18n/RO/query-builder-resources.ts | 2 ++ .../src/i18n/SV/query-builder-resources.ts | 2 ++ .../src/i18n/TR/query-builder-resources.ts | 2 ++ .../i18n/ZH-HANS/query-builder-resources.ts | 2 ++ .../i18n/ZH-HANT/query-builder-resources.ts | 2 ++ .../lib/core/i18n/query-builder-resources.ts | 4 +++ .../data-operations/filtering-condition.ts | 6 ++-- .../query-builder.component.html | 24 ++++++------- .../query-builder/query-builder.component.ts | 35 +++++++------------ 23 files changed, 67 insertions(+), 40 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts index 7fb3e8faf64..8ce12dfe63a 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts @@ -15,6 +15,8 @@ const QueryBuilderResourceStringsBG_: ExpandRequire) => this.findValueInSet(target, searchVal) }, { name: 'notIn', isUnary: false, - iconName: 'starts-with', - // hidden: true, + iconName: 'not-empty', logic: (target: any, searchVal: Set) => !this.findValueInSet(target, searchVal) }]; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 4baee0569c9..b7bc1286043 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -175,6 +175,7 @@
*ngIf="expressionItem.inEditMode" #editingInputsContainer class="igx-filter-tree__inputs" + style = "margin-top: 10px" > @@ -431,19 +432,16 @@
- - All - - {{ field.label || field.header || field.field }} - - + placeholder="Select return fields" + searchPlaceholder="Search..."> +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 16716e22363..42568adae8f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -37,7 +37,7 @@ import { IgxPrefixDirective } from '../directives/prefix/prefix.directive'; import { IgxIconComponent } from '../icon/icon.component'; import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; -import { ISelectionEventArgs } from '../drop-down/drop-down.common'; +import { IgxComboComponent } from "../combo/combo.component"; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; @@ -117,7 +117,7 @@ class ExpressionOperandItem extends ExpressionItem { selector: 'igx-query-builder', templateUrl: './query-builder.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent] }) export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { /** @@ -228,9 +228,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { @ViewChildren('entitySelect', { read: IgxSelectComponent }) protected entitySelect: IgxSelectComponent; - @ViewChild('returnFieldsSelect', { read: IgxSelectComponent }) - protected returnFieldsSelect: IgxSelectComponent; - @ViewChild('fieldSelect', { read: IgxSelectComponent }) private fieldSelect: IgxSelectComponent; @@ -369,15 +366,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { modal: false, closeOnOutsideClick: false }; - - /** - * @hidden @internal - */ - public returnFieldsSelectOverlaySettings: OverlaySettings = { - scrollStrategy: new AbsoluteScrollStrategy(), - modal: false, - closeOnOutsideClick: false - }; /** * @hidden @internal @@ -441,7 +429,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { public ngAfterViewInit(): void { this._overlaySettings.outlet = this.overlayOutlet; this.entitySelectOverlaySettings.outlet = this.overlayOutlet; - this.returnFieldsSelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; } @@ -498,10 +485,14 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { public set selectedReturnFields(value: string | string[]) { const oldValue = this._selectedReturnFields; + // if (!value || value.length === 0) { + // value = this.fields.map(f => f.field); + // } + if (this._selectedReturnFields !== value && oldValue !== value) { this._selectedReturnFields = value; if (this.expressionTree) { - this.expressionTree.returnFields = value; + this.expressionTree.returnFields = value.length === 0 ? '*' : value; this.expressionTreeChange.emit(); } } @@ -756,9 +747,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.entitySelectOverlaySettings.target = this.entitySelect.element; this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); - this.returnFieldsSelectOverlaySettings.target = this.returnFieldsSelect.element; - this.returnFieldsSelectOverlaySettings.excludeFromOutsideClick = [this.returnFieldsSelect.element as HTMLElement]; - this.returnFieldsSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); this.fieldSelectOverlaySettings.target = this.fieldSelect.element; this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); @@ -1136,10 +1124,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); - this.selectedReturnFields = - !expressionTree.returnFields || expressionTree.returnFields === "*" || expressionTree.returnFields === "All" - ? 'All' - : this.fields.find(f => f.field === expressionTree.returnFields).field; + console.log('stop') + this._selectedReturnFields = + !expressionTree.returnFields || expressionTree.returnFields === '*' || expressionTree.returnFields === 'All' + ? this.fields.map(f => f.field) + : this.fields.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } } } From 70c4b9e3b4f058d36de9cc1f9553364842fbdc8e Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 6 Aug 2024 10:54:10 +0300 Subject: [PATCH 005/147] feat(query-builder): add icons for 'in' and 'not-in' conditions --- .../data-operations/filtering-condition.ts | 4 ++-- .../query-builder/query-builder.component.ts | 22 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts index 6136126a2d8..b46da3b07d5 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts @@ -22,12 +22,12 @@ export class IgxFilteringOperand { }, { name: 'in', isUnary: false, - iconName: 'is-empty', + iconName: 'in', logic: (target: any, searchVal: Set) => this.findValueInSet(target, searchVal) }, { name: 'notIn', isUnary: false, - iconName: 'not-empty', + iconName: 'not-in', logic: (target: any, searchVal: Set) => !this.findValueInSet(target, searchVal) }]; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 42568adae8f..b61e33c3613 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -468,7 +468,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.expressionTree.entity = this.selectedEntity.name; this.expressionTree.returnFields = null; - // TODO: reset conditions on entity change + // TODO: show dialog alert } } @@ -653,12 +653,10 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public operandCanBeCommitted(): boolean { - return true; // TODO: enable for search tree + // TODO: fix when creating inner query - console.log(this._editedExpression.fieldLabel); - console.log(this._editedExpression.expression); return this.selectedField && this.selectedCondition && - (!!this.searchValue || this.selectedField.filters.condition(this.selectedCondition).isUnary); + (!!this.searchValue || !!this._editedExpression.expression.searchTree || this.selectedField.filters.condition(this.selectedCondition).isUnary); } /** @@ -1341,6 +1339,20 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { }); }); + const inIcon = ''; + this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons'); + this.iconService.addIconRef('in', 'default', { + name: 'in', + family: 'imx-icons' + }); + + const notInIcon = ''; + this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons'); + this.iconService.addIconRef('not-in', 'default', { + name: 'not-in', + family: 'imx-icons' + }); + this.iconService.addIconRef('add', 'default', { name: 'add', family: 'material', From 830d4493b7bf898a90bc274e4ab5a58befcf35c2 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 6 Aug 2024 15:26:23 +0300 Subject: [PATCH 006/147] chore(*): fix setting search tree on commit --- .../src/lib/query-builder/query-builder.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index b61e33c3613..2f192f82bc2 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -601,12 +601,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public commitOperandEdit() { - console.log('commitOperandEdit'); if (this._editedExpression) { this._editedExpression.expression.fieldName = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); - if (this.innerQuery) { + if (this.innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; } this._editedExpression.fieldLabel = this.selectedField.label From d0e6f29020f1e5ff69c1c88a9f0ebee48f9fcb84 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 7 Aug 2024 12:42:41 +0300 Subject: [PATCH 007/147] feat(query-builder): add labels for inputs and some fixes --- .../query-builder.component.html | 79 +++++++++---------- .../query-builder/query-builder.component.ts | 24 +++--- 2 files changed, 46 insertions(+), 57 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index b7bc1286043..1a54a4859b1 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -94,15 +94,15 @@
(selectedChanged)="onChipSelectionEnd()" > - {{this.expressionTree.entity}} / {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} + {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} - + {{ getConditionFriendlyName( @@ -114,26 +114,30 @@
igxSuffix *ngIf="!expressionItem.expression.condition.isUnary" > + + {{expressionItem.expression.searchTree.entity}} / {{expressionItem.expression.searchTree.returnFields}} + + + {{ - expressionItem.expression.searchTree - ? (expressionItem.expression.searchTree.entity + ' / ' + expressionItem.expression.searchTree.returnFields) - : isDate(expressionItem.expression.searchVal) - ? getFormatter(expressionItem.expression.fieldName) - ? (expressionItem.expression.searchVal - | fieldFormatter - : getFormatter( - expressionItem.expression.fieldName - ) - : undefined) - : (expressionItem.expression.searchVal - | date - : getFormat( - expressionItem.expression.fieldName - ) - : undefined - : this.locale) - : expressionItem.expression.searchVal + isDate(expressionItem.expression.searchVal) + ? getFormatter(expressionItem.expression.fieldName) + ? (expressionItem.expression.searchVal + | fieldFormatter + : getFormatter( + expressionItem.expression.fieldName + ) + : undefined) + : (expressionItem.expression.searchVal + | date + : getFormat( + expressionItem.expression.fieldName + ) + : undefined + : this.locale) + : expressionItem.expression.searchVal }} +
#fieldSelect type="box" [overlaySettings]="fieldSelectOverlaySettings" - [placeholder]=" - this.resourceStrings.igx_query_builder_column_placeholder - " [(ngModel)]="selectedField" [disabled]="!fields" > + {{ field.label || field.header || field.field }} @@ -196,12 +198,10 @@
#conditionSelect type="box" [overlaySettings]="conditionSelectOverlaySettings" - [placeholder]=" - this.resourceStrings.igx_query_builder_condition_placeholder - " [(ngModel)]="selectedCondition" [disabled]="!selectedField" > + " type="box" > + (selectedField && selectedField.filters.condition(selectedCondition) .isUnary) || - selectedCondition === 'in' + selectedCondition === 'in' || + selectedCondition === 'notIn' " [type]=" selectedField && selectedField.dataType === 'number' ? 'number' : 'text' " - [placeholder]=" - this.resourceStrings.igx_query_builder_value_placeholder - " [(ngModel)]="searchValue" /> @@ -308,9 +307,6 @@
[(value)]="searchValue" (click)="picker.open()" (keydown)="openPicker($event)" - [placeholder]=" - this.resourceStrings.igx_query_builder_time_placeholder - " type="box" [readOnly]="true" [disabled]=" @@ -326,6 +322,7 @@
[inputFormat]="selectedField.defaultTimeFormat" > + @@ -336,13 +333,11 @@
*ngIf="selectedField && selectedField.dataType === 'dateTime'" type="box" > +
- + + (expressionTreeChange)="onInnerQueryExpressionSearchTreeChange()"> @@ -422,11 +417,9 @@
+ {{entity.name}} @@ -439,8 +432,8 @@
[displayKey]="'field'" [valueKey]="'field'" [(ngModel)]="selectedReturnFields" - placeholder="Select return fields" searchPlaceholder="Search..."> +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 2f192f82bc2..4c06d6579bd 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -38,6 +38,7 @@ import { IgxIconComponent } from '../icon/icon.component'; import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; import { IgxComboComponent } from "../combo/combo.component"; +import { IgxLabelDirective } from '../input-group/public_api'; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; @@ -117,7 +118,7 @@ class ExpressionOperandItem extends ExpressionItem { selector: 'igx-query-builder', templateUrl: './query-builder.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective] }) export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { /** @@ -225,7 +226,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { @Output() public expressionTreeChange = new EventEmitter(); - @ViewChildren('entitySelect', { read: IgxSelectComponent }) + @ViewChild('entitySelect', { read: IgxSelectComponent }) protected entitySelect: IgxSelectComponent; @ViewChild('fieldSelect', { read: IgxSelectComponent }) @@ -607,6 +608,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); if (this.innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; + } else { + this._editedExpression.expression.searchTree = null; } this._editedExpression.fieldLabel = this.selectedField.label ? this.selectedField.label @@ -714,8 +717,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public enterExpressionEdit(expressionItem: ExpressionOperandItem) { - console.log('enterExpressionEdit'); - // console.log(this.expressionTree); this.clearSelection(); this.exitOperandEdit(); this.cancelOperandAdd(); @@ -725,10 +726,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } expressionItem.hovered = false; - if (this.expressionTree) { - this._selectedEntity = this.entities.find(entity => entity.name === this.expressionTree.entity); - this.selectedReturnFields = this.expressionTree.returnFields; - } this.fields = this.selectedEntity ? this.selectedEntity.fields : null; this.selectedField = expressionItem.expression.fieldName ? this.fields.find(field => field.field === expressionItem.expression.fieldName) : null; @@ -1002,11 +999,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } - public onExpressionSearchTreeChange() { - console.log('onExpressionSearchTreeChange'); - console.log(this.expressionTree); - this.expressionTreeChange.emit(); - // this.cdr.detectChanges(); + public onInnerQueryExpressionSearchTreeChange() { + if (this._editedExpression && this._editedExpression.expression) { + this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; + } + // this.expressionTreeChange.emit(); } private setFormat(field: FieldType) { @@ -1121,7 +1118,6 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); - console.log('stop') this._selectedReturnFields = !expressionTree.returnFields || expressionTree.returnFields === '*' || expressionTree.returnFields === 'All' ? this.fields.map(f => f.field) From 5b2a24ac15cb63db12fd39b368db8942114ab479 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 7 Aug 2024 13:35:45 +0300 Subject: [PATCH 008/147] feat(query-builder): trim return fields in chip if many --- .../query-builder.component.html | 43 ++++++++++--------- .../query-builder/query-builder.component.ts | 9 ++++ 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 1a54a4859b1..bdea084116d 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -115,7 +115,7 @@
*ngIf="!expressionItem.expression.condition.isUnary" > - {{expressionItem.expression.searchTree.entity}} / {{expressionItem.expression.searchTree.returnFields}} + {{expressionItem.expression.searchTree.entity}} / {{formatReturnFields(expressionItem.expression.searchTree.returnFields)}} @@ -414,27 +414,28 @@
- - - - {{entity.name}} - - + + + + + {{entity.name}} + + - - - + + + +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 4c06d6579bd..f393856bad3 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -1006,6 +1006,15 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { // this.expressionTreeChange.emit(); } + public formatReturnFields(returnFields: string | string[]) { + let text = returnFields; + if (Array.isArray(returnFields)) { + text = returnFields.join(', '); + text = text.length > 25 ? text.substring(0, 25) + ' ...' : text; + } + return text; + } + private setFormat(field: FieldType) { if (!field.pipeArgs) { field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; From 98b08dfd8eefa26f81c8d7a5f92b5c8ecd6ee73f Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Tue, 6 Aug 2024 16:23:20 +0300 Subject: [PATCH 009/147] feat(query-builder): Added request to backend on tree change --- .../query-builder/query-builder.sample.html | 5 +++ src/app/query-builder/query-builder.sample.ts | 40 +++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index c6a6d9001b5..9e7bde39583 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -3,6 +3,7 @@ @@ -13,6 +14,10 @@
--> +
+ {{ this.queryResult }} +
+
{{ printExpressionTree(queryBuilder.expressionTree) }}
diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 3db2c5c4987..14049f82fcb 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -1,14 +1,17 @@ import { Component, ViewChild, OnInit } from '@angular/core'; -import { FilteringExpressionsTree, IgxStringFilteringOperand, +import { + FilteringExpressionsTree, IgxStringFilteringOperand, FilteringLogic, IgxQueryBuilderComponent, changei18n, IExpressionTree, IgxButtonDirective, IgxButtonGroupComponent, - IgxRippleDirective} from 'igniteui-angular'; + IgxRippleDirective +} from 'igniteui-angular'; import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; +import { CommonModule } from '@angular/common'; @Component({ providers: [], @@ -16,7 +19,7 @@ import { SizeSelectorComponent } from '../size-selector/size-selector.component' styleUrls: ['query-builder.sample.scss'], templateUrl: 'query-builder.sample.html', standalone: true, - imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent] + imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent, CommonModule] }) export class QueryBuilderComponent implements OnInit { @ViewChild('queryBuilder', { static: true }) @@ -26,10 +29,12 @@ export class QueryBuilderComponent implements OnInit { public fields: Array; public displayDensities; public expressionTree: IExpressionTree; + public queryResult: string; + private backendUrl = "http://localhost:3333/"; public ngOnInit(): void { this.entities = [ - { + { name: 'Assays', fields: [ { field: 'Id', dataType: 'number' }, { field: 'CompoundId', dataType: 'number' }, @@ -61,10 +66,10 @@ export class QueryBuilderComponent implements OnInit { }); const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', '*'); tree.filteringOperands.push({ - fieldName: 'Id', - condition: IgxStringFilteringOperand.instance().condition('in'), - searchTree: innerTree - }); + fieldName: 'Id', + condition: IgxStringFilteringOperand.instance().condition('in'), + searchTree: innerTree + }); tree.filteringOperands.push({ fieldName: 'Structure', condition: IgxStringFilteringOperand.instance().condition('contains'), @@ -93,6 +98,21 @@ export class QueryBuilderComponent implements OnInit { // }); this.expressionTree = tree; + this.onChange(); + } + + public async onChange() { + const tree = JSON.stringify(this.expressionTree); + const resp = await fetch(this.backendUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: tree + }) + if (resp.status == 200) { + this.queryResult = await resp.text(); + } } public changeLocale(locale: string) { @@ -103,6 +123,10 @@ export class QueryBuilderComponent implements OnInit { } public printExpressionTree(tree: IExpressionTree) { + if (JSON.stringify(tree) !== JSON.stringify(this.expressionTree)) { + this.expressionTree = tree; + this.onChange(); + } return tree ? JSON.stringify(tree, null, 2) : 'Please add an expression!'; } } From 7005a4d48de56e9d71676a1fbd2ae1c5c7b7e1a8 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 7 Aug 2024 15:20:12 +0300 Subject: [PATCH 010/147] feat(query-builder): hide entity and return fields selects in inner groups --- .../src/lib/query-builder/query-builder.component.html | 4 ++-- .../src/lib/query-builder/query-builder.component.ts | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index bdea084116d..70cccb78a83 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -373,7 +373,7 @@
style="margin: 10px 0px 10px 0px" [entities]="entities" [expressionTree]="expressionItem.expression.searchTree" - (expressionTreeChange)="onInnerQueryExpressionSearchTreeChange()"> + (expressionTreeChange)="onInnerQueryExpressionSearchTreeChange(expressionItem.expression)"> @@ -414,7 +414,7 @@
- + Date: Wed, 7 Aug 2024 16:42:26 +0300 Subject: [PATCH 011/147] chore(*): fix update of search tree --- .../query-builder/query-builder.component.html | 2 +- .../query-builder/query-builder.component.ts | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 70cccb78a83..a465427c9d2 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -373,7 +373,7 @@
style="margin: 10px 0px 10px 0px" [entities]="entities" [expressionTree]="expressionItem.expression.searchTree" - (expressionTreeChange)="onInnerQueryExpressionSearchTreeChange(expressionItem.expression)"> + (expressionTreeChange)="onInnerQueryExpressionSearchTreeChange(expressionItem.expression, innerQuery)"> diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 1f26ca15be1..05ea12c7d4d 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -606,11 +606,11 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this._editedExpression.expression.fieldName = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); - if (this.innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { - this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; - } else { - this._editedExpression.expression.searchTree = null; - } + // if (this.innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { + // this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; + // } else { + // this._editedExpression.expression.searchTree = null; + // } this._editedExpression.fieldLabel = this.selectedField.label ? this.selectedField.label : this.selectedField.header @@ -999,8 +999,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { } } - public onInnerQueryExpressionSearchTreeChange(expression: any) { - expression.searchTree = this.innerQuery.expressionTree; + public onInnerQueryExpressionSearchTreeChange(expression: any, innerQuery: any) { + expression.searchTree = innerQuery.expressionTree; this.expressionTreeChange.emit(); } @@ -1121,7 +1121,9 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); const entity = this.entities.find(el => el.name === expressionTree.entity); - this.fields = entity.fields; + if (entity) { + this.fields = entity.fields; + } const field = this.fields.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); From efb44c8b336990c095a54f228f0a7ca31aae21c3 Mon Sep 17 00:00:00 2001 From: gedinakova Date: Wed, 7 Aug 2024 16:56:59 +0300 Subject: [PATCH 012/147] chore(*): Revert "feat(query-builder): Added request to backend on tree change" This reverts commit 98b08dfd8eefa26f81c8d7a5f92b5c8ecd6ee73f. --- .../query-builder/query-builder.sample.html | 5 --- src/app/query-builder/query-builder.sample.ts | 40 ++++--------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 9e7bde39583..c6a6d9001b5 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -3,7 +3,6 @@ @@ -14,10 +13,6 @@
--> -
- {{ this.queryResult }} -
-
{{ printExpressionTree(queryBuilder.expressionTree) }}
diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 14049f82fcb..3db2c5c4987 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -1,17 +1,14 @@ import { Component, ViewChild, OnInit } from '@angular/core'; -import { - FilteringExpressionsTree, IgxStringFilteringOperand, +import { FilteringExpressionsTree, IgxStringFilteringOperand, FilteringLogic, IgxQueryBuilderComponent, changei18n, IExpressionTree, IgxButtonDirective, IgxButtonGroupComponent, - IgxRippleDirective -} from 'igniteui-angular'; + IgxRippleDirective} from 'igniteui-angular'; import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; -import { CommonModule } from '@angular/common'; @Component({ providers: [], @@ -19,7 +16,7 @@ import { CommonModule } from '@angular/common'; styleUrls: ['query-builder.sample.scss'], templateUrl: 'query-builder.sample.html', standalone: true, - imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent, CommonModule] + imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent] }) export class QueryBuilderComponent implements OnInit { @ViewChild('queryBuilder', { static: true }) @@ -29,12 +26,10 @@ export class QueryBuilderComponent implements OnInit { public fields: Array; public displayDensities; public expressionTree: IExpressionTree; - public queryResult: string; - private backendUrl = "http://localhost:3333/"; public ngOnInit(): void { this.entities = [ - { + { name: 'Assays', fields: [ { field: 'Id', dataType: 'number' }, { field: 'CompoundId', dataType: 'number' }, @@ -66,10 +61,10 @@ export class QueryBuilderComponent implements OnInit { }); const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', '*'); tree.filteringOperands.push({ - fieldName: 'Id', - condition: IgxStringFilteringOperand.instance().condition('in'), - searchTree: innerTree - }); + fieldName: 'Id', + condition: IgxStringFilteringOperand.instance().condition('in'), + searchTree: innerTree + }); tree.filteringOperands.push({ fieldName: 'Structure', condition: IgxStringFilteringOperand.instance().condition('contains'), @@ -98,21 +93,6 @@ export class QueryBuilderComponent implements OnInit { // }); this.expressionTree = tree; - this.onChange(); - } - - public async onChange() { - const tree = JSON.stringify(this.expressionTree); - const resp = await fetch(this.backendUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: tree - }) - if (resp.status == 200) { - this.queryResult = await resp.text(); - } } public changeLocale(locale: string) { @@ -123,10 +103,6 @@ export class QueryBuilderComponent implements OnInit { } public printExpressionTree(tree: IExpressionTree) { - if (JSON.stringify(tree) !== JSON.stringify(this.expressionTree)) { - this.expressionTree = tree; - this.onChange(); - } return tree ? JSON.stringify(tree, null, 2) : 'Please add an expression!'; } } From c99619bcea8989f8fdce48cabca6bbafa1c97ebd Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 13 Aug 2024 20:10:20 +0300 Subject: [PATCH 013/147] feat(query-builder): extract tree into separate component --- .../query-builder/_query-builder-theme.scss | 2 +- .../filtering-expression.interface.ts | 3 +- .../query-builder-tree.component.html | 557 +++++++ .../query-builder-tree.component.ts | 1403 +++++++++++++++++ .../query-builder.component.html | 562 +------ .../query-builder/query-builder.component.ts | 1254 +-------------- src/app/query-builder/query-builder.sample.ts | 18 +- 7 files changed, 2023 insertions(+), 1776 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html create mode 100644 projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 2881ac0584f..3cbdf6a3c0a 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -120,7 +120,7 @@ %advanced-filter__main { display: block; overflow: auto; - min-height: pad(rem(138px), rem(164px), rem(214px)); + // min-height: pad(rem(138px), rem(164px), rem(214px)); // max-height: pad(rem(305px), rem(394px), rem(468px)); [igxButton] + [igxButton] { diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index 2218fdd64e1..07362251b12 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -1,3 +1,4 @@ +import { IExpressionTree } from 'igniteui-angular'; import { IFilteringOperation } from './filtering-condition'; /* mustCoerceToInt */ @@ -15,6 +16,6 @@ export declare interface IFilteringExpression { fieldName: string; condition: IFilteringOperation; searchVal?: any; - searchTree?: any; + searchTree?: IExpressionTree; ignoreCase?: boolean; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html new file mode 100644 index 00000000000..da3bc096fb6 --- /dev/null +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -0,0 +1,557 @@ + + + + + + + + +
+ + + + + +
+
+ {{ this.resourceStrings.igx_query_builder_initial_text }} +
+
+
+ + + + + + + + + + +
+ + + {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} + + + + {{ + getConditionFriendlyName( + expressionItem.expression.condition.name + ) + }} + + + + {{expressionItem.expression.searchTree.entity}} / {{formatReturnFields(expressionItem.expression.searchTree.returnFields)}} + + + + {{ + isDate(expressionItem.expression.searchVal) + ? getFormatter(expressionItem.expression.fieldName) + ? (expressionItem.expression.searchVal + | fieldFormatter + : getFormatter( + expressionItem.expression.fieldName + ) + : undefined) + : (expressionItem.expression.searchVal + | date + : getFormat( + expressionItem.expression.fieldName + ) + : undefined + : this.locale) + : expressionItem.expression.searchVal + }} + + + + +
+ + + + +
+
+
+ +
+ + + + {{ field.label || field.header || field.field }} + + + + + + + + + + + +
+ + + {{ + getConditionFriendlyName(condition) + }} +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + + +
+ + + +
+
+ + +
+
+ +
+
+ + + + + {{entity.name}} + + + + + + + +
+ + + + + +
+ + + +
+
+
+
+ + + + + +
+ + + + + + + + + + + + + + +
+
+ +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts new file mode 100644 index 00000000000..54240bcbfdc --- /dev/null +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -0,0 +1,1403 @@ +import { AfterViewInit, ContentChild, EventEmitter, LOCALE_ID, Output, Pipe, PipeTransform } from '@angular/core'; +import { getLocaleFirstDayOfWeek, NgIf, NgFor, NgTemplateOutlet, NgClass, DatePipe } from '@angular/common'; +import { Inject } from '@angular/core'; +import { + Component, Input, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef, OnDestroy, HostBinding +} from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Subject } from 'rxjs'; +import { editor } from '@igniteui/material-icons-extended'; +import { IButtonGroupEventArgs, IgxButtonGroupComponent } from '../buttonGroup/buttonGroup.component'; +import { IgxChipComponent } from '../chips/chip.component'; +import { IQueryBuilderResourceStrings, QueryBuilderResourceStringsEN } from '../core/i18n/query-builder-resources'; +import { PlatformUtil } from '../core/utils'; +import { DataType, DataUtil } from '../data-operations/data-util'; +import { IgxBooleanFilteringOperand, IgxDateFilteringOperand, IgxDateTimeFilteringOperand, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxTimeFilteringOperand } from '../data-operations/filtering-condition'; +import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface'; +import { FilteringExpressionsTree, IExpressionTree } from '../data-operations/filtering-expressions-tree'; +import { IgxDatePickerComponent } from '../date-picker/date-picker.component'; + +import { IgxButtonDirective } from '../directives/button/button.directive'; +import { IgxDateTimeEditorDirective } from '../directives/date-time-editor/date-time-editor.directive'; + +import { IgxOverlayOutletDirective, IgxToggleActionDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive'; +import { FieldType, EntityType } from '../grids/common/grid.interface'; +import { IgxIconService } from '../icon/icon.service'; +import { IgxSelectComponent } from '../select/select.component'; +import { HorizontalAlignment, OverlaySettings, Point, VerticalAlignment } from '../services/overlay/utilities'; +import { AbsoluteScrollStrategy, AutoPositionStrategy, CloseScrollStrategy, ConnectedPositioningStrategy } from '../services/public_api'; +import { IgxTimePickerComponent } from '../time-picker/time-picker.component'; +import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component'; +import { IgxPickerToggleComponent, IgxPickerClearComponent } from '../date-common/picker-icons.common'; +import { IgxInputDirective } from '../directives/input/input.directive'; +import { IgxInputGroupComponent } from '../input-group/input-group.component'; +import { IgxSelectItemComponent } from '../select/select-item.component'; +import { IgxSuffixDirective } from '../directives/suffix/suffix.directive'; +import { IgxPrefixDirective } from '../directives/prefix/prefix.directive'; +import { IgxIconComponent } from '../icon/icon.component'; +import { getCurrentResourceStrings } from '../core/i18n/resources'; +import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; +import { IgxComboComponent } from "../combo/combo.component"; +import { IgxLabelDirective } from '../input-group/public_api'; + +const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; +const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; +const DEFAULT_PIPE_DATE_TIME_FORMAT = 'medium'; +const DEFAULT_PIPE_DIGITS_INFO = '1.0-3'; +const DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm:ss a'; +const DEFAULT_TIME_FORMAT = 'hh:mm:ss a'; + +@Pipe({ + name: 'fieldFormatter', + standalone: true +}) +export class IgxFieldFormatterPipe implements PipeTransform { + + public transform(value: any, formatter: (v: any, data: any, fieldData?: any) => any, rowData: any, fieldData?: any) { + return formatter(value, rowData, fieldData); + } +} + +/** + * @hidden @internal + * + * Internal class usage + */ +class ExpressionItem { + public parent: ExpressionGroupItem; + public selected: boolean; + constructor(parent?: ExpressionGroupItem) { + this.parent = parent; + } +} + +/** + * @hidden @internal + * + * Internal class usage + */ +class ExpressionGroupItem extends ExpressionItem { + public operator: FilteringLogic; + public children: ExpressionItem[]; + constructor(operator: FilteringLogic, parent?: ExpressionGroupItem) { + super(parent); + this.operator = operator; + this.children = []; + } +} + +/** + * @hidden @internal + * + * Internal class usage + */ +class ExpressionOperandItem extends ExpressionItem { + public expression: IFilteringExpression; + public inEditMode: boolean; + public inAddMode: boolean; + public hovered: boolean; + public fieldLabel: string; + constructor(expression: IFilteringExpression, parent: ExpressionGroupItem) { + super(parent); + this.expression = expression; + } +} + +@Component({ + selector: 'igx-query-builder-tree', + templateUrl: './query-builder-tree.component.html', + standalone: true, + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective] +}) +export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { + /** + * @hidden @internal + */ + @HostBinding('class.igx-query-builder') + public cssClass = 'igx-query-builder'; + + /** + * @hidden @internal + */ + @HostBinding('style.display') + public display = 'block'; + + @Input() + public entities: EntityType[]; + + @Input() + public get parentExpression(): ExpressionOperandItem { + return this._parentExpression; + } + + public set parentExpression(value: ExpressionOperandItem) { + this._parentExpression = value; + } + + /** + * Returns the fields. + */ + public get fields(): FieldType[] { + return this._fields; + } + + /** + * Sets the fields. + */ + @Input() + public set fields(fields: FieldType[]) { + this._fields = fields; + + if (this._fields) { + this.registerSVGIcons(); + + this._fields.forEach(field => { + this.setFilters(field); + this.setFormat(field); + }); + } + } + + /** + * Returns the expression tree. + */ + public get expressionTree(): IExpressionTree { + return this._expressionTree; + } + + /** + * Sets the expression tree. + */ + @Input() + public set expressionTree(expressionTree: IExpressionTree) { + this._expressionTree = expressionTree; + + this.init(); + } + + /** + * Gets the `locale` of the query builder. + * If not set, defaults to application's locale. + */ + @Input() + public get locale(): string { + return this._locale; + } + + /** + * Sets the `locale` of the query builder. + * Expects a valid BCP 47 language tag. + */ + public set locale(value: string) { + this._locale = value; + // if value is invalid, set it back to _localeId + try { + getLocaleFirstDayOfWeek(this._locale); + } catch (e) { + this._locale = this._localeId; + } + } + + /** + * Sets the resource strings. + * By default it uses EN resources. + */ + @Input() + public set resourceStrings(value: IQueryBuilderResourceStrings) { + this._resourceStrings = Object.assign({}, this._resourceStrings, value); + } + + /** + * Returns the resource strings. + */ + public get resourceStrings(): IQueryBuilderResourceStrings { + return this._resourceStrings; + } + + /** + * Event fired as the expression tree is changed. + * + * ```html + * + * ``` + */ + @Output() + public expressionTreeChange = new EventEmitter(); + + @ViewChild('entitySelect', { read: IgxSelectComponent }) + protected entitySelect: IgxSelectComponent; + + @ViewChild('fieldSelect', { read: IgxSelectComponent }) + private fieldSelect: IgxSelectComponent; + + @ViewChild('conditionSelect', { read: IgxSelectComponent }) + private conditionSelect: IgxSelectComponent; + + @ViewChild('searchValueInput', { read: ElementRef }) + private searchValueInput: ElementRef; + + @ViewChild('picker') + private picker: IgxDatePickerComponent | IgxTimePickerComponent; + + @ViewChild('addRootAndGroupButton', { read: ElementRef }) + private addRootAndGroupButton: ElementRef; + + @ViewChild('addConditionButton', { read: ElementRef }) + private addConditionButton: ElementRef; + + /** + * @hidden @internal + */ + @ContentChild(IgxQueryBuilderHeaderComponent) + public headerContent: IgxQueryBuilderHeaderComponent; + + @ViewChild('editingInputsContainer', { read: ElementRef }) + protected set editingInputsContainer(value: ElementRef) { + if ((value && !this._editingInputsContainer) || + (value && this._editingInputsContainer && this._editingInputsContainer.nativeElement !== value.nativeElement)) { + requestAnimationFrame(() => { + this.scrollElementIntoView(value.nativeElement); + }); + } + + this._editingInputsContainer = value; + } + + /** @hidden */ + protected get editingInputsContainer(): ElementRef { + return this._editingInputsContainer; + } + + @ViewChild('addModeContainer', { read: ElementRef }) + protected set addModeContainer(value: ElementRef) { + if ((value && !this._addModeContainer) || + (value && this._addModeContainer && this._addModeContainer.nativeElement !== value.nativeElement)) { + requestAnimationFrame(() => { + this.scrollElementIntoView(value.nativeElement); + }); + } + + this._addModeContainer = value; + } + + /** @hidden */ + protected get addModeContainer(): ElementRef { + return this._addModeContainer; + } + + @ViewChild('currentGroupButtonsContainer', { read: ElementRef }) + protected set currentGroupButtonsContainer(value: ElementRef) { + if ((value && !this._currentGroupButtonsContainer) || + (value && this._currentGroupButtonsContainer && this._currentGroupButtonsContainer.nativeElement !== value.nativeElement)) { + requestAnimationFrame(() => { + this.scrollElementIntoView(value.nativeElement); + }); + } + + this._currentGroupButtonsContainer = value; + } + + /** @hidden */ + protected get currentGroupButtonsContainer(): ElementRef { + return this._currentGroupButtonsContainer; + } + + @ViewChild(IgxToggleDirective) + private contextMenuToggle: IgxToggleDirective; + + @ViewChildren(IgxChipComponent) + private chips: QueryList; + + @ViewChild('expressionsContainer') + private expressionsContainer: ElementRef; + + @ViewChild('overlayOutlet', { read: IgxOverlayOutletDirective, static: true }) + private overlayOutlet: IgxOverlayOutletDirective; + + @ViewChildren(IgxQueryBuilderTreeComponent) + private innerQueries: QueryList; + + /** + * @hidden @internal + */ + public rootGroup: ExpressionGroupItem; + + /** + * @hidden @internal + */ + public selectedExpressions: ExpressionOperandItem[] = []; + + /** + * @hidden @internal + */ + public currentGroup: ExpressionGroupItem; + + /** + * @hidden @internal + */ + public contextualGroup: ExpressionGroupItem; + + /** + * @hidden @internal + */ + public filteringLogics; + + /** + * @hidden @internal + */ + public selectedCondition: string; + + /** + * @hidden @internal + */ + public searchValue: any; + + /** + * @hidden @internal + */ + public pickerOutlet: IgxOverlayOutletDirective | ElementRef; + + /** + * @hidden @internal + */ + public entitySelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: false + }; + + /** + * @hidden @internal + */ + public fieldSelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: false + }; + + /** + * @hidden @internal + */ + public conditionSelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: false + }; + + private destroy$ = new Subject(); + private _parentExpression: ExpressionOperandItem; + private _initialExpressionTree: IExpressionTree; + private _selectedEntity: EntityType; + private _selectedReturnFields: string | string[]; + private _selectedField: FieldType; + private _clickTimer; + private _dblClickDelay = 200; + private _preventChipClick = false; + private _editingInputsContainer: ElementRef; + private _addModeContainer: ElementRef; + private _currentGroupButtonsContainer: ElementRef; + private _addModeExpression: ExpressionOperandItem; + private _editedExpression: ExpressionOperandItem; + private _selectedGroups: ExpressionGroupItem[] = []; + private _fields: FieldType[]; + private _expressionTree: IExpressionTree; + private _locale; + private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); + + private _positionSettings = { + horizontalStartPoint: HorizontalAlignment.Right, + verticalStartPoint: VerticalAlignment.Top + }; + + private _overlaySettings: OverlaySettings = { + closeOnOutsideClick: false, + modal: false, + positionStrategy: new ConnectedPositioningStrategy(this._positionSettings), + scrollStrategy: new CloseScrollStrategy() + }; + + constructor(public cdr: ChangeDetectorRef, + protected iconService: IgxIconService, + protected platform: PlatformUtil, + protected el: ElementRef, + @Inject(LOCALE_ID) protected _localeId: string) { + this.locale = this.locale || this._localeId; + } + + /** + * @hidden @internal + */ + public ngAfterViewInit(): void { + this._overlaySettings.outlet = this.overlayOutlet; + this.entitySelectOverlaySettings.outlet = this.overlayOutlet; + this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; + this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; + } + + /** + * @hidden @internal + */ + public ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } + + /** + * @hidden @internal + */ + public set selectedEntity(value: EntityType) { + let oldValue = null; + if (this.expressionTree) { + oldValue = this.expressionTree.entity; + } + if (this._selectedEntity !== value) { + this._selectedEntity = value; + this.selectedReturnFields = null; + this.fields = value ? value.fields : null; + if (oldValue && this._selectedEntity /*&& this._selectedEntity !== oldValue*/) { + this.selectedField = null; + this.selectedCondition = null; + this.searchValue = null; + this.cdr.detectChanges(); + } + } + + const entity = this.entities.find(el => el.name === this.selectedEntity.name); + this.fields = entity.fields; + + if (this.expressionTree) { + this.expressionTree.entity = this.selectedEntity.name; + this.expressionTree.returnFields = null; + + // TODO: show dialog alert + } + } + + /** + * @hidden @internal + */ + public get selectedEntity(): EntityType { + return this._selectedEntity; + } + + /** + * @hidden @internal + */ + public set selectedReturnFields(value: string | string[]) { + const oldValue = this._selectedReturnFields; + + if (this._selectedReturnFields !== value && oldValue !== value) { + this._selectedReturnFields = value; + // if (this._expressionTree) { + // this._expressionTree.returnFields = value.length === 0 ? '*' : value; + // // this.expressionTreeChange.emit(); + // } + } + } + + /** + * @hidden @internal + */ + public get selectedReturnFields(): string | string[] { + return this._selectedReturnFields; + } + + /** + * @hidden @internal + */ + public set selectedField(value: FieldType) { + const oldValue = this._selectedField; + + if (this._selectedField !== value) { + this._selectedField = value; + if (oldValue && this._selectedField && this._selectedField.dataType !== oldValue.dataType) { + this.selectedCondition = null; + this.searchValue = null; + this.cdr.detectChanges(); + } + } + } + + /** + * @hidden @internal + */ + public get selectedField(): FieldType { + return this._selectedField; + } + + /** + * @hidden @internal + * + * used by the grid + */ + public setPickerOutlet(outlet?: IgxOverlayOutletDirective | ElementRef) { + this.pickerOutlet = outlet; + } + + /** + * @hidden @internal + * + * used by the grid + */ + public get isContextMenuVisible(): boolean { + return !this.contextMenuToggle.collapsed; + } + + /** + * @hidden @internal + */ + public get hasEditedExpression(): boolean { + return this._editedExpression !== undefined && this._editedExpression !== null; + } + + /** + * @hidden @internal + */ + public addCondition(parent: ExpressionGroupItem, afterExpression?: ExpressionItem) { + this.cancelOperandAdd(); + + const operandItem = new ExpressionOperandItem({ + fieldName: null, + condition: null, + ignoreCase: true, + searchVal: null + }, parent); + + if (afterExpression) { + const index = parent.children.indexOf(afterExpression); + parent.children.splice(index + 1, 0, operandItem); + } else { + parent.children.push(operandItem); + } + + this.enterExpressionEdit(operandItem); + } + + /** + * @hidden @internal + */ + public addAndGroup(parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) { + this.addGroup(FilteringLogic.And, parent, afterExpression); + } + + /** + * @hidden @internal + */ + public addOrGroup(parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) { + this.addGroup(FilteringLogic.Or, parent, afterExpression); + } + + /** + * @hidden @internal + */ + public endGroup(groupItem: ExpressionGroupItem) { + this.currentGroup = groupItem.parent; + } + + /** + * @hidden @internal + */ + public commitOperandEdit() { + if (this._editedExpression) { + this._editedExpression.expression.fieldName = this.selectedField.field; + this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); + this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); + this._editedExpression.fieldLabel = this.selectedField.label + ? this.selectedField.label + : this.selectedField.header + ? this.selectedField.header + : this.selectedField.field; + + const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0] + if (innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { + this._editedExpression.expression.searchTree.entity = innerQuery.selectedEntity.name; + this._editedExpression.expression.searchTree.returnFields = innerQuery.selectedReturnFields; + this._editedExpression.expression.searchTree.filteringOperands = innerQuery.expressionTree.filteringOperands; + } else { + this._editedExpression.expression.searchTree = null; + } + + this._editedExpression.inEditMode = false; + this._editedExpression = null; + } + + this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); + this._expressionTree.entity = this.selectedEntity.name; + this._expressionTree.returnFields = this.selectedReturnFields; + this.expressionTreeChange.emit(); + } + + /** + * @hidden @internal + */ + public cancelOperandAdd() { + if (this._addModeExpression) { + this._addModeExpression.inAddMode = false; + this._addModeExpression = null; + } + } + + /** + * @hidden @internal + */ + public cancelOperandEdit() { + if (this.innerQueries) { + const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; + if (innerQuery) { + innerQuery.expressionTree = this._initialExpressionTree; + } + } + + if (this._editedExpression) { + this._editedExpression.inEditMode = false; + + if (!this._editedExpression.expression.fieldName) { + this.deleteItem(this._editedExpression); + } + + this._editedExpression = null; + } + } + + /** + * @hidden @internal + */ + public operandCanBeCommitted(): boolean { + // TODO: fix when creating inner query + + return this.selectedField && this.selectedCondition && + (!!this.searchValue || !!this._editedExpression.expression.searchTree || this.selectedField.filters.condition(this.selectedCondition).isUnary); + } + + /** + * @hidden @internal + * + * used by the grid + */ + public exitOperandEdit() { + if (!this._editedExpression) { + return; + } + + if (this.operandCanBeCommitted()) { + this.commitOperandEdit(); + } else { + this.cancelOperandEdit(); + } + } + + /** + * @hidden @internal + */ + public isExpressionGroup(expression: ExpressionItem): boolean { + return expression instanceof ExpressionGroupItem; + } + + /** + * @hidden @internal + */ + public onChipRemove(expressionItem: ExpressionItem) { + this.deleteItem(expressionItem); + } + + /** + * @hidden @internal + */ + public onChipClick(expressionItem: ExpressionOperandItem) { + this._clickTimer = setTimeout(() => { + if (!this._preventChipClick) { + this.onToggleExpression(expressionItem); + } + this._preventChipClick = false; + }, this._dblClickDelay); + } + + /** + * @hidden @internal + */ + public onChipDblClick(expressionItem: ExpressionOperandItem) { + clearTimeout(this._clickTimer); + this._preventChipClick = true; + this.enterExpressionEdit(expressionItem); + } + + /** + * @hidden @internal + */ + public enterExpressionEdit(expressionItem: ExpressionOperandItem) { + this.clearSelection(); + this.exitOperandEdit(); + this.cancelOperandAdd(); + + if (this._editedExpression) { + this._editedExpression.inEditMode = false; + } + + expressionItem.hovered = false; + this.fields = this.selectedEntity ? this.selectedEntity.fields : null; + this.selectedField = + expressionItem.expression.fieldName ? + this.fields.find(field => field.field === expressionItem.expression.fieldName) + : null; + this.selectedCondition = + expressionItem.expression.condition ? + expressionItem.expression.condition.name : + null; + this.searchValue = expressionItem.expression.searchVal; + + expressionItem.inEditMode = true; + this._editedExpression = expressionItem; + if (expressionItem.expression.searchTree) { + this._initialExpressionTree = expressionItem.expression.searchTree; + } else { + this._initialExpressionTree = null; + } + + this.cdr.detectChanges(); + + this.entitySelectOverlaySettings.target = this.entitySelect.element; + this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; + this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + this.fieldSelectOverlaySettings.target = this.fieldSelect.element; + this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; + this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + this.conditionSelectOverlaySettings.target = this.conditionSelect.element; + this.conditionSelectOverlaySettings.excludeFromOutsideClick = [this.conditionSelect.element as HTMLElement]; + this.conditionSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + + if (!this.selectedField) { + this.fieldSelect.input.nativeElement.focus(); + } else if (this.selectedField.filters.condition(this.selectedCondition).isUnary) { + this.conditionSelect.input.nativeElement.focus(); + } else { + const input = this.searchValueInput?.nativeElement || this.picker?.getEditElement(); + input.focus(); + } + } + + /** + * @hidden @internal + */ + public clearSelection() { + for (const group of this._selectedGroups) { + group.selected = false; + } + this._selectedGroups = []; + + for (const expr of this.selectedExpressions) { + expr.selected = false; + } + this.selectedExpressions = []; + + this.toggleContextMenu(); + } + + /** + * @hidden @internal + */ + public enterExpressionAdd(expressionItem: ExpressionOperandItem) { + this.clearSelection(); + this.exitOperandEdit(); + + if (this._addModeExpression) { + this._addModeExpression.inAddMode = false; + } + + expressionItem.inAddMode = true; + this._addModeExpression = expressionItem; + if (expressionItem.selected) { + this.toggleExpression(expressionItem); + } + } + + /** + * @hidden @internal + */ + public contextMenuClosed() { + this.contextualGroup = null; + } + + /** + * @hidden @internal + */ + public onKeyDown(eventArgs: KeyboardEvent) { + eventArgs.stopPropagation(); + const key = eventArgs.key; + if (!this.contextMenuToggle.collapsed && (key === this.platform.KEYMAP.ESCAPE)) { + this.clearSelection(); + } + } + + /** + * @hidden @internal + */ + public createAndGroup() { + this.createGroup(FilteringLogic.And); + } + + /** + * @hidden @internal + */ + public createOrGroup() { + this.createGroup(FilteringLogic.Or); + } + + /** + * @hidden @internal + */ + public deleteFilters() { + for (const expr of this.selectedExpressions) { + this.deleteItem(expr); + } + + this.clearSelection(); + } + + /** + * @hidden @internal + */ + public onGroupClick(groupItem: ExpressionGroupItem) { + this.toggleGroup(groupItem); + } + + /** + * @hidden @internal + */ + public ungroup() { + const selectedGroup = this.contextualGroup; + const parent = selectedGroup.parent; + if (parent) { + const index = parent.children.indexOf(selectedGroup); + parent.children.splice(index, 1, ...selectedGroup.children); + + for (const expr of selectedGroup.children) { + expr.parent = parent; + } + } + + this.clearSelection(); + this.commitOperandEdit(); + } + + /** + * @hidden @internal + */ + public deleteGroup() { + const selectedGroup = this.contextualGroup; + const parent = selectedGroup.parent; + if (parent) { + const index = parent.children.indexOf(selectedGroup); + parent.children.splice(index, 1); + } else { + this.rootGroup = null; + } + + this.clearSelection(); + this.commitOperandEdit(); + } + + /** + * @hidden @internal + */ + public selectFilteringLogic(event: IButtonGroupEventArgs) { + this.contextualGroup.operator = event.index as FilteringLogic; + this.commitOperandEdit(); + } + + /** + * @hidden @internal + */ + public getConditionFriendlyName(name: string): string { + return this.resourceStrings[`igx_query_builder_filter_${name}`] || name; + } + + /** + * @hidden @internal + */ + public isDate(value: any) { + return value instanceof Date; + } + + /** + * @hidden @internal + */ + public onExpressionsScrolled() { + if (!this.contextMenuToggle.collapsed) { + this.calculateContextMenuTarget(); + this.contextMenuToggle.reposition(); + } + } + + /** + * @hidden @internal + */ + public invokeClick(eventArgs: KeyboardEvent) { + if (this.platform.isActivationKey(eventArgs)) { + eventArgs.preventDefault(); + (eventArgs.currentTarget as HTMLElement).click(); + } + } + + /** + * @hidden @internal + */ + public openPicker(args: KeyboardEvent) { + if (this.platform.isActivationKey(args)) { + args.preventDefault(); + this.picker.open(); + } + } + + /** + * @hidden @internal + */ + public onOutletPointerDown(event) { + // This prevents closing the select's dropdown when clicking the scroll + event.preventDefault(); + } + + /** + * @hidden @internal + */ + public getConditionList(): string[] { + return this.selectedField ? this.selectedField.filters.conditionList() : []; + } + + /** + * @hidden @internal + */ + public getFormatter(field: string) { + return this.fields.find(el => el.field === field).formatter; + } + + /** + * @hidden @internal + */ + public getFormat(field: string) { + return this.fields.find(el => el.field === field).pipeArgs.format; + } + + /** + * @hidden @internal + * + * used by the grid + */ + public setAddButtonFocus() { + if (this.addRootAndGroupButton) { + this.addRootAndGroupButton.nativeElement.focus(); + } else if (this.addConditionButton) { + this.addConditionButton.nativeElement.focus(); + } + } + + /** + * @hidden @internal + */ + public context(expression: ExpressionItem, afterExpression?: ExpressionItem) { + return { + $implicit: expression, + afterExpression + }; + } + + /** + * @hidden @internal + */ + public onChipSelectionEnd() { + const contextualGroup = this.findSingleSelectedGroup(); + if (contextualGroup || this.selectedExpressions.length > 1) { + this.contextualGroup = contextualGroup; + this.calculateContextMenuTarget(); + if (this.contextMenuToggle.collapsed) { + this.contextMenuToggle.open(this._overlaySettings); + } else { + this.contextMenuToggle.reposition(); + } + } + } + + public formatReturnFields(returnFields: string | string[]) { + + let text = returnFields; + if (Array.isArray(returnFields)) { + text = returnFields.join(', '); + text = text.length > 25 ? text.substring(0, 25) + ' ...' : text; + } + return text; + } + + public isInEditMode(): boolean { + return !this.parentExpression || (this.parentExpression && this.parentExpression.inEditMode); + } + + private setFormat(field: FieldType) { + if (!field.pipeArgs) { + field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; + } + + if (!field.pipeArgs.format) { + field.pipeArgs.format = field.dataType === DataType.Time ? + DEFAULT_PIPE_TIME_FORMAT : field.dataType === DataType.DateTime ? + DEFAULT_PIPE_DATE_TIME_FORMAT : DEFAULT_PIPE_DATE_FORMAT; + } + + if (!field.defaultDateTimeFormat) { + field.defaultDateTimeFormat = DEFAULT_DATE_TIME_FORMAT; + } + + if (!field.defaultTimeFormat) { + field.defaultTimeFormat = DEFAULT_TIME_FORMAT; + } + } + + private setFilters(field: FieldType) { + if (!field.filters) { + switch (field.dataType) { + case DataType.Boolean: + field.filters = IgxBooleanFilteringOperand.instance(); + break; + case DataType.Number: + case DataType.Currency: + case DataType.Percent: + field.filters = IgxNumberFilteringOperand.instance(); + break; + case DataType.Date: + field.filters = IgxDateFilteringOperand.instance(); + break; + case DataType.Time: + field.filters = IgxTimeFilteringOperand.instance(); + break; + case DataType.DateTime: + field.filters = IgxDateTimeFilteringOperand.instance(); + break; + case DataType.String: + default: + field.filters = IgxStringFilteringOperand.instance(); + break; + } + + } + } + + private onToggleExpression(expressionItem: ExpressionOperandItem) { + this.exitOperandEdit(); + this.toggleExpression(expressionItem); + + this.toggleContextMenu(); + } + + private toggleExpression(expressionItem: ExpressionOperandItem) { + expressionItem.selected = !expressionItem.selected; + + if (expressionItem.selected) { + this.selectedExpressions.push(expressionItem); + } else { + const index = this.selectedExpressions.indexOf(expressionItem); + this.selectedExpressions.splice(index, 1); + this.deselectParentRecursive(expressionItem); + } + } + + private addGroup(operator: FilteringLogic, parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) { + this.cancelOperandAdd(); + + const groupItem = new ExpressionGroupItem(operator, parent); + + if (parent) { + if (afterExpression) { + const index = parent.children.indexOf(afterExpression); + parent.children.splice(index + 1, 0, groupItem); + } else { + parent.children.push(groupItem); + } + } else { + this.rootGroup = groupItem; + } + + this.addCondition(groupItem); + this.currentGroup = groupItem; + } + + private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem): ExpressionGroupItem { + let groupItem: ExpressionGroupItem; + if (expressionTree) { + groupItem = new ExpressionGroupItem(expressionTree.operator, parent); + + for (const expr of expressionTree.filteringOperands) { + if (expr instanceof FilteringExpressionsTree) { + groupItem.children.push(this.createExpressionGroupItem(expr, groupItem)); + } else { + const filteringExpr = expr as IFilteringExpression; + const exprCopy: IFilteringExpression = { + fieldName: filteringExpr.fieldName, + condition: filteringExpr.condition, + searchVal: filteringExpr.searchVal, + searchTree: filteringExpr.searchTree, // this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), + ignoreCase: filteringExpr.ignoreCase + }; + const operandItem = new ExpressionOperandItem(exprCopy, groupItem); + const entity = this.entities.find(el => el.name === expressionTree.entity); + if (entity) { + this.fields = entity.fields; + } + const field = this.fields.find(el => el.field === filteringExpr.fieldName); + operandItem.fieldLabel = field.label || field.header || field.field; + groupItem.children.push(operandItem); + this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); + this._selectedReturnFields = + !expressionTree.returnFields || expressionTree.returnFields === '*' || expressionTree.returnFields === 'All' + ? this.fields.map(f => f.field) + : this.fields.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); + } + } + } + return groupItem; + } + + private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem): FilteringExpressionsTree { + if (!groupItem) { + return null; + } + + const expressionTree = new FilteringExpressionsTree(groupItem.operator); + + for (const item of groupItem.children) { + if (item instanceof ExpressionGroupItem) { + const subTree = this.createExpressionTreeFromGroupItem((item as ExpressionGroupItem)); + expressionTree.filteringOperands.push(subTree); + } else { + expressionTree.filteringOperands.push((item as ExpressionOperandItem).expression); + } + } + + return expressionTree; + } + + private toggleContextMenu() { + const contextualGroup = this.findSingleSelectedGroup(); + + if (contextualGroup || this.selectedExpressions.length > 1) { + this.contextualGroup = contextualGroup; + + if (contextualGroup) { + this.filteringLogics = [ + { + label: this.resourceStrings.igx_query_builder_filter_operator_and, + selected: contextualGroup.operator === FilteringLogic.And + }, + { + label: this.resourceStrings.igx_query_builder_filter_operator_or, + selected: contextualGroup.operator === FilteringLogic.Or + } + ]; + } + } else if (this.contextMenuToggle) { + this.contextMenuToggle.close(); + } + } + + private findSingleSelectedGroup(): ExpressionGroupItem { + for (const group of this._selectedGroups) { + const containsAllSelectedExpressions = this.selectedExpressions.every(op => this.isInsideGroup(op, group)); + + if (containsAllSelectedExpressions) { + return group; + } + } + + return null; + } + + private isInsideGroup(item: ExpressionItem, group: ExpressionGroupItem): boolean { + if (!item) { + return false; + } + + if (item.parent === group) { + return true; + } + + return this.isInsideGroup(item.parent, group); + } + + private deleteItem(expressionItem: ExpressionItem) { + if (!expressionItem.parent) { + this.rootGroup = null; + this.currentGroup = null; + this._expressionTree = null; + return; + } + + if (expressionItem === this.currentGroup) { + this.currentGroup = this.currentGroup.parent; + } + + const children = expressionItem.parent.children; + const index = children.indexOf(expressionItem); + children.splice(index, 1); + const entity = this.expressionTree ? this.expressionTree.entity : null; + const returnFields = this.expressionTree ? this.expressionTree.returnFields : null; + this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); // TODO: don't recreate if not necessary + this._expressionTree.entity = entity; + this._expressionTree.returnFields = returnFields; + + if (!children.length) { + this.deleteItem(expressionItem.parent); + } + + this.expressionTreeChange.emit(); + } + + private createGroup(operator: FilteringLogic) { + const chips = this.chips.toArray(); + const minIndex = this.selectedExpressions.reduce((i, e) => Math.min(i, chips.findIndex(c => c.data === e)), Number.MAX_VALUE); + const firstExpression = chips[minIndex].data; + + const parent = firstExpression.parent; + const groupItem = new ExpressionGroupItem(operator, parent); + + const index = parent.children.indexOf(firstExpression); + parent.children.splice(index, 0, groupItem); + + for (const expr of this.selectedExpressions) { + groupItem.children.push(expr); + this.deleteItem(expr); + expr.parent = groupItem; + } + + this.clearSelection(); + } + + private toggleGroup(groupItem: ExpressionGroupItem) { + this.exitOperandEdit(); + if (groupItem.children && groupItem.children.length) { + this.toggleGroupRecursive(groupItem, !groupItem.selected); + if (!groupItem.selected) { + this.deselectParentRecursive(groupItem); + } + this.toggleContextMenu(); + } + } + + private toggleGroupRecursive(groupItem: ExpressionGroupItem, selected: boolean) { + if (groupItem.selected !== selected) { + groupItem.selected = selected; + + if (groupItem.selected) { + this._selectedGroups.push(groupItem); + } else { + const index = this._selectedGroups.indexOf(groupItem); + this._selectedGroups.splice(index, 1); + } + } + + for (const expr of groupItem.children) { + if (expr instanceof ExpressionGroupItem) { + this.toggleGroupRecursive(expr, selected); + } else { + const operandExpression = expr as ExpressionOperandItem; + if (operandExpression.selected !== selected) { + this.toggleExpression(operandExpression); + } + } + } + } + + private deselectParentRecursive(expressionItem: ExpressionItem) { + const parent = expressionItem.parent; + if (parent) { + if (parent.selected) { + parent.selected = false; + const index = this._selectedGroups.indexOf(parent); + this._selectedGroups.splice(index, 1); + } + this.deselectParentRecursive(parent); + } + } + + private calculateContextMenuTarget() { + const containerRect = this.expressionsContainer.nativeElement.getBoundingClientRect(); + const chips = this.chips.filter(c => this.selectedExpressions.indexOf(c.data) !== -1); + let minTop = chips.reduce((t, c) => + Math.min(t, c.nativeElement.getBoundingClientRect().top), Number.MAX_VALUE); + minTop = Math.max(containerRect.top, minTop); + minTop = Math.min(containerRect.bottom, minTop); + let maxRight = chips.reduce((r, c) => + Math.max(r, c.nativeElement.getBoundingClientRect().right), 0); + maxRight = Math.max(maxRight, containerRect.left); + maxRight = Math.min(maxRight, containerRect.right); + this._overlaySettings.target = new Point(maxRight, minTop); + } + + private scrollElementIntoView(target: HTMLElement) { + const container = this.expressionsContainer.nativeElement; + const targetOffset = target.offsetTop - container.offsetTop; + const delta = 10; + + if (container.scrollTop + delta > targetOffset) { + container.scrollTop = targetOffset - delta; + } else if (container.scrollTop + container.clientHeight < targetOffset + target.offsetHeight + delta) { + container.scrollTop = targetOffset + target.offsetHeight + delta - container.clientHeight; + } + } + + private init() { + this.clearSelection(); + this.cancelOperandAdd(); + this.cancelOperandEdit(); + this.rootGroup = this.createExpressionGroupItem(this.expressionTree); + this.currentGroup = this.rootGroup; + } + + private registerSVGIcons(): void { + const editorIcons = editor as any[]; + + editorIcons.forEach((icon) => { + this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons'); + this.iconService.addIconRef(icon.name, 'default', { + name: icon.name, + family: 'imx-icons' + }); + }); + + const inIcon = ''; + this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons'); + this.iconService.addIconRef('in', 'default', { + name: 'in', + family: 'imx-icons' + }); + + const notInIcon = ''; + this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons'); + this.iconService.addIconRef('not-in', 'default', { + name: 'not-in', + family: 'imx-icons' + }); + + this.iconService.addIconRef('add', 'default', { + name: 'add', + family: 'material', + }); + + this.iconService.addIconRef('close', 'default', { + name: 'close', + family: 'material', + }); + + this.iconService.addIconRef('check', 'default', { + name: 'check', + family: 'material', + }); + + this.iconService.addIconRef('delete', 'default', { + name: 'delete', + family: 'material', + }); + + this.iconService.addIconRef('edit', 'default', { + name: 'edit', + family: 'material', + }); + } +} + diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index a465427c9d2..2818b9109fa 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -1,561 +1,15 @@ - - - - - - - - - - -
- - - - - -
-
- {{ this.resourceStrings.igx_query_builder_initial_text }} -
-
-
- - - - - - - - - - -
- - - {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} - - - - {{ - getConditionFriendlyName( - expressionItem.expression.condition.name - ) - }} - - - - {{expressionItem.expression.searchTree.entity}} / {{formatReturnFields(expressionItem.expression.searchTree.returnFields)}} - - - - {{ - isDate(expressionItem.expression.searchVal) - ? getFormatter(expressionItem.expression.fieldName) - ? (expressionItem.expression.searchVal - | fieldFormatter - : getFormatter( - expressionItem.expression.fieldName - ) - : undefined) - : (expressionItem.expression.searchVal - | date - : getFormat( - expressionItem.expression.fieldName - ) - : undefined - : this.locale) - : expressionItem.expression.searchVal - }} - - - -
- - - - -
-
- -
- - - - {{ field.label || field.header || field.field }} - - - - - - - - - - - -
- - - {{ - getConditionFriendlyName(condition) - }} -
-
-
- - - - - + - - - - - - - - - - - - - - - - - - -
- - -
-
- - - - - - -
- - - -
-
- - -
-
- -
-
- - - - - {{entity.name}} - - - - - - - -
- - - - - -
- - - -
-
-
-
- - - - - -
- - - - - - - - - - - - - - -
-
- -
+ \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 05ea12c7d4d..a57ff957e17 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -39,69 +39,7 @@ import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; import { IgxComboComponent } from "../combo/combo.component"; import { IgxLabelDirective } from '../input-group/public_api'; - -const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; -const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; -const DEFAULT_PIPE_DATE_TIME_FORMAT = 'medium'; -const DEFAULT_PIPE_DIGITS_INFO = '1.0-3'; -const DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm:ss a'; -const DEFAULT_TIME_FORMAT = 'hh:mm:ss a'; - -@Pipe({ - name: 'fieldFormatter', - standalone: true -}) -export class IgxFieldFormatterPipe implements PipeTransform { - - public transform(value: any, formatter: (v: any, data: any, fieldData?: any) => any, rowData: any, fieldData?: any) { - return formatter(value, rowData, fieldData); - } -} - -/** - * @hidden @internal - * - * Internal class usage - */ -class ExpressionItem { - public parent: ExpressionGroupItem; - public selected: boolean; - constructor(parent?: ExpressionGroupItem) { - this.parent = parent; - } -} - -/** - * @hidden @internal - * - * Internal class usage - */ -class ExpressionGroupItem extends ExpressionItem { - public operator: FilteringLogic; - public children: ExpressionItem[]; - constructor(operator: FilteringLogic, parent?: ExpressionGroupItem) { - super(parent); - this.operator = operator; - this.children = []; - } -} - -/** - * @hidden @internal - * - * Internal class usage - */ -class ExpressionOperandItem extends ExpressionItem { - public expression: IFilteringExpression; - public inEditMode: boolean; - public inAddMode: boolean; - public hovered: boolean; - public fieldLabel: string; - constructor(expression: IFilteringExpression, parent: ExpressionGroupItem) { - super(parent); - this.expression = expression; - } -} +import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component'; /** * A component used for operating with complex filters by creating or editing conditions @@ -118,7 +56,7 @@ class ExpressionOperandItem extends ExpressionItem { selector: 'igx-query-builder', templateUrl: './query-builder.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxQueryBuilderTreeComponent] }) export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { /** @@ -136,69 +74,40 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { @Input() public entities: EntityType[]; - /** - * Returns the fields. - */ - public get fields(): FieldType[] { - return this._fields; - } + // /** + // * Returns the fields. + // */ + // public get fields(): FieldType[] { + // return this._fields; + // } + + // /** + // * Sets the fields. + // */ + // @Input() + // public set fields(fields: FieldType[]) { + // this._fields = fields; + + // if (this._fields) { + // this.registerSVGIcons(); + + // this._fields.forEach(field => { + // this.setFilters(field); + // this.setFormat(field); + // }); + // } + // } - /** - * Sets the fields. - */ - @Input() - public set fields(fields: FieldType[]) { - this._fields = fields; - - if (this._fields) { - this.registerSVGIcons(); - - this._fields.forEach(field => { - this.setFilters(field); - this.setFormat(field); - }); - } - } - - /** - * Returns the expression tree. - */ - public get expressionTree(): IExpressionTree { - return this._expressionTree; - } - - /** - * Sets the expression tree. - */ + @Input() - public set expressionTree(expressionTree: IExpressionTree) { - this._expressionTree = expressionTree; - - this.init(); - } + public expressionTree: IExpressionTree; /** * Gets the `locale` of the query builder. * If not set, defaults to application's locale. */ @Input() - public get locale(): string { - return this._locale; - } - - /** - * Sets the `locale` of the query builder. - * Expects a valid BCP 47 language tag. - */ - public set locale(value: string) { - this._locale = value; - // if value is invalid, set it back to _localeId - try { - getLocaleFirstDayOfWeek(this._locale); - } catch (e) { - this._locale = this._localeId; - } - } + public locale: string; /** * Sets the resource strings. @@ -225,213 +134,27 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { */ @Output() public expressionTreeChange = new EventEmitter(); - - @ViewChild('entitySelect', { read: IgxSelectComponent }) - protected entitySelect: IgxSelectComponent; - - @ViewChild('fieldSelect', { read: IgxSelectComponent }) - private fieldSelect: IgxSelectComponent; - - @ViewChild('conditionSelect', { read: IgxSelectComponent }) - private conditionSelect: IgxSelectComponent; - - @ViewChild('searchValueInput', { read: ElementRef }) - private searchValueInput: ElementRef; - - @ViewChild('picker') - private picker: IgxDatePickerComponent | IgxTimePickerComponent; - - @ViewChild('addRootAndGroupButton', { read: ElementRef }) - private addRootAndGroupButton: ElementRef; - - @ViewChild('addConditionButton', { read: ElementRef }) - private addConditionButton: ElementRef; /** * @hidden @internal */ @ContentChild(IgxQueryBuilderHeaderComponent) public headerContent: IgxQueryBuilderHeaderComponent; - - @ViewChild('editingInputsContainer', { read: ElementRef }) - protected set editingInputsContainer(value: ElementRef) { - if ((value && !this._editingInputsContainer) || - (value && this._editingInputsContainer && this._editingInputsContainer.nativeElement !== value.nativeElement)) { - requestAnimationFrame(() => { - this.scrollElementIntoView(value.nativeElement); - }); - } - - this._editingInputsContainer = value; - } - - /** @hidden */ - protected get editingInputsContainer(): ElementRef { - return this._editingInputsContainer; - } - - @ViewChild('addModeContainer', { read: ElementRef }) - protected set addModeContainer(value: ElementRef) { - if ((value && !this._addModeContainer) || - (value && this._addModeContainer && this._addModeContainer.nativeElement !== value.nativeElement)) { - requestAnimationFrame(() => { - this.scrollElementIntoView(value.nativeElement); - }); - } - - this._addModeContainer = value; - } - - /** @hidden */ - protected get addModeContainer(): ElementRef { - return this._addModeContainer; - } - - @ViewChild('currentGroupButtonsContainer', { read: ElementRef }) - protected set currentGroupButtonsContainer(value: ElementRef) { - if ((value && !this._currentGroupButtonsContainer) || - (value && this._currentGroupButtonsContainer && this._currentGroupButtonsContainer.nativeElement !== value.nativeElement)) { - requestAnimationFrame(() => { - this.scrollElementIntoView(value.nativeElement); - }); - } - - this._currentGroupButtonsContainer = value; - } - - /** @hidden */ - protected get currentGroupButtonsContainer(): ElementRef { - return this._currentGroupButtonsContainer; - } - - @ViewChild(IgxToggleDirective) - private contextMenuToggle: IgxToggleDirective; - - @ViewChildren(IgxChipComponent) - private chips: QueryList; - - @ViewChild('expressionsContainer') - private expressionsContainer: ElementRef; - - @ViewChild('overlayOutlet', { read: IgxOverlayOutletDirective, static: true }) - private overlayOutlet: IgxOverlayOutletDirective; - - @ViewChild(IgxQueryBuilderComponent) - private innerQuery: IgxQueryBuilderComponent; - - /** - * @hidden @internal - */ - public rootGroup: ExpressionGroupItem; - - /** - * @hidden @internal - */ - public selectedExpressions: ExpressionOperandItem[] = []; - - /** - * @hidden @internal - */ - public currentGroup: ExpressionGroupItem; - - /** - * @hidden @internal - */ - public contextualGroup: ExpressionGroupItem; - - /** - * @hidden @internal - */ - public filteringLogics; - - /** - * @hidden @internal - */ - public selectedCondition: string; - - /** - * @hidden @internal - */ - public searchValue: any; - - /** - * @hidden @internal - */ - public pickerOutlet: IgxOverlayOutletDirective | ElementRef; - - /** - * @hidden @internal - */ - public entitySelectOverlaySettings: OverlaySettings = { - scrollStrategy: new AbsoluteScrollStrategy(), - modal: false, - closeOnOutsideClick: false - }; - - /** - * @hidden @internal - */ - public fieldSelectOverlaySettings: OverlaySettings = { - scrollStrategy: new AbsoluteScrollStrategy(), - modal: false, - closeOnOutsideClick: false - }; - + /** * @hidden @internal */ - public conditionSelectOverlaySettings: OverlaySettings = { - scrollStrategy: new AbsoluteScrollStrategy(), - modal: false, - closeOnOutsideClick: false - }; - + @ViewChild(IgxQueryBuilderTreeComponent) + public queryTree: IgxQueryBuilderTreeComponent; + private destroy$ = new Subject(); - private _selectedEntity: EntityType; - private _selectedReturnFields: string | string[]; - private _selectedField: FieldType; - private _clickTimer; - private _dblClickDelay = 200; - private _preventChipClick = false; - private _editingInputsContainer: ElementRef; - private _addModeContainer: ElementRef; - private _currentGroupButtonsContainer: ElementRef; - private _addModeExpression: ExpressionOperandItem; - private _editedExpression: ExpressionOperandItem; - private _selectedGroups: ExpressionGroupItem[] = []; - private _fields: FieldType[]; - private _expressionTree: IExpressionTree; - private _locale; private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); - private _positionSettings = { - horizontalStartPoint: HorizontalAlignment.Right, - verticalStartPoint: VerticalAlignment.Top - }; - - private _overlaySettings: OverlaySettings = { - closeOnOutsideClick: false, - modal: false, - positionStrategy: new ConnectedPositioningStrategy(this._positionSettings), - scrollStrategy: new CloseScrollStrategy() - }; - - constructor(public cdr: ChangeDetectorRef, - protected iconService: IgxIconService, - protected platform: PlatformUtil, - protected el: ElementRef, - @Inject(LOCALE_ID) protected _localeId: string) { - this.locale = this.locale || this._localeId; - } - /** * @hidden @internal */ public ngAfterViewInit(): void { - this._overlaySettings.outlet = this.overlayOutlet; - this.entitySelectOverlaySettings.outlet = this.overlayOutlet; - this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; - this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; + // this._overlaySettings.outlet = this.overlayOutlet; } /** @@ -442,100 +165,13 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.destroy$.complete(); } - /** - * @hidden @internal - */ - public set selectedEntity(value: EntityType) { - let oldValue = null; - if (this.expressionTree) { - oldValue = this.expressionTree.entity; - } - if (this._selectedEntity !== value) { - this._selectedEntity = value; - this.selectedReturnFields = null; - this.fields = value ? value.fields : null; - if (oldValue && this._selectedEntity /*&& this._selectedEntity !== oldValue*/) { - this.selectedField = null; - this.selectedCondition = null; - this.searchValue = null; - this.cdr.detectChanges(); - } - } - - const entity = this.entities.find(el => el.name === this.selectedEntity.name); - this.fields = entity.fields; - - if (this.expressionTree) { - this.expressionTree.entity = this.selectedEntity.name; - this.expressionTree.returnFields = null; - - // TODO: show dialog alert - } - } - - /** - * @hidden @internal - */ - public get selectedEntity(): EntityType { - return this._selectedEntity; - } - - /** - * @hidden @internal - */ - public set selectedReturnFields(value: string | string[]) { - const oldValue = this._selectedReturnFields; - - // if (!value || value.length === 0) { - // value = this.fields.map(f => f.field); - // } - - if (this._selectedReturnFields !== value && oldValue !== value) { - this._selectedReturnFields = value; - if (this.expressionTree) { - this.expressionTree.returnFields = value.length === 0 ? '*' : value; - this.expressionTreeChange.emit(); - } - } - } - - /** - * @hidden @internal - */ - public get selectedReturnFields(): string | string[] { - return this._selectedReturnFields; - } - - /** - * @hidden @internal - */ - public set selectedField(value: FieldType) { - const oldValue = this._selectedField; - - if (this._selectedField !== value) { - this._selectedField = value; - if (oldValue && this._selectedField && this._selectedField.dataType !== oldValue.dataType) { - this.selectedCondition = null; - this.searchValue = null; - this.cdr.detectChanges(); - } - } - } - - /** - * @hidden @internal - */ - public get selectedField(): FieldType { - return this._selectedField; - } - /** * @hidden @internal * * used by the grid */ public setPickerOutlet(outlet?: IgxOverlayOutletDirective | ElementRef) { - this.pickerOutlet = outlet; + this.queryTree.setPickerOutlet(outlet); } /** @@ -544,121 +180,14 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * used by the grid */ public get isContextMenuVisible(): boolean { - return !this.contextMenuToggle.collapsed; - } - - /** - * @hidden @internal - */ - public get hasEditedExpression(): boolean { - return this._editedExpression !== undefined && this._editedExpression !== null; - } - - /** - * @hidden @internal - */ - public addCondition(parent: ExpressionGroupItem, afterExpression?: ExpressionItem) { - this.cancelOperandAdd(); - - const operandItem = new ExpressionOperandItem({ - fieldName: null, - condition: null, - ignoreCase: true, - searchVal: null - }, parent); - - if (afterExpression) { - const index = parent.children.indexOf(afterExpression); - parent.children.splice(index + 1, 0, operandItem); - } else { - parent.children.push(operandItem); - } - - this.enterExpressionEdit(operandItem); - } - - /** - * @hidden @internal - */ - public addAndGroup(parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) { - this.addGroup(FilteringLogic.And, parent, afterExpression); - } - - /** - * @hidden @internal - */ - public addOrGroup(parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) { - this.addGroup(FilteringLogic.Or, parent, afterExpression); - } - - /** - * @hidden @internal - */ - public endGroup(groupItem: ExpressionGroupItem) { - this.currentGroup = groupItem.parent; - } - - /** - * @hidden @internal - */ - public commitOperandEdit() { - if (this._editedExpression) { - this._editedExpression.expression.fieldName = this.selectedField.field; - this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); - this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); - // if (this.innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { - // this._editedExpression.expression.searchTree = this.innerQuery.expressionTree; - // } else { - // this._editedExpression.expression.searchTree = null; - // } - this._editedExpression.fieldLabel = this.selectedField.label - ? this.selectedField.label - : this.selectedField.header - ? this.selectedField.header - : this.selectedField.field; - this._editedExpression.inEditMode = false; - this._editedExpression = null; - } - - this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); - this.expressionTree.entity = this.selectedEntity.name; - this.expressionTree.returnFields = this.selectedReturnFields; - this.expressionTreeChange.emit(); - } - - /** - * @hidden @internal - */ - public cancelOperandAdd() { - if (this._addModeExpression) { - this._addModeExpression.inAddMode = false; - this._addModeExpression = null; - } - } - - /** - * @hidden @internal - */ - public cancelOperandEdit() { - if (this._editedExpression) { - this._editedExpression.inEditMode = false; - - if (!this._editedExpression.expression.fieldName) { - this.deleteItem(this._editedExpression); - } - - this._editedExpression = null; - } + return this.queryTree.isContextMenuVisible; } /** * @hidden @internal */ - public operandCanBeCommitted(): boolean { - // TODO: fix when creating inner query - - return this.selectedField && this.selectedCondition && - (!!this.searchValue || !!this._editedExpression.expression.searchTree || this.selectedField.filters.condition(this.selectedCondition).isUnary); + public clearSelection() { + this.queryTree.clearSelection(); } /** @@ -667,297 +196,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * used by the grid */ public exitOperandEdit() { - if (!this._editedExpression) { - return; - } - - if (this.operandCanBeCommitted()) { - this.commitOperandEdit(); - } else { - this.cancelOperandEdit(); - } - } - - /** - * @hidden @internal - */ - public isExpressionGroup(expression: ExpressionItem): boolean { - return expression instanceof ExpressionGroupItem; - } - - /** - * @hidden @internal - */ - public onChipRemove(expressionItem: ExpressionItem) { - this.deleteItem(expressionItem); - } - - /** - * @hidden @internal - */ - public onChipClick(expressionItem: ExpressionOperandItem) { - this._clickTimer = setTimeout(() => { - if (!this._preventChipClick) { - this.onToggleExpression(expressionItem); - } - this._preventChipClick = false; - }, this._dblClickDelay); - } - - /** - * @hidden @internal - */ - public onChipDblClick(expressionItem: ExpressionOperandItem) { - clearTimeout(this._clickTimer); - this._preventChipClick = true; - this.enterExpressionEdit(expressionItem); - } - - /** - * @hidden @internal - */ - public enterExpressionEdit(expressionItem: ExpressionOperandItem) { - this.clearSelection(); - this.exitOperandEdit(); - this.cancelOperandAdd(); - - if (this._editedExpression) { - this._editedExpression.inEditMode = false; - } - - expressionItem.hovered = false; - this.fields = this.selectedEntity ? this.selectedEntity.fields : null; - this.selectedField = expressionItem.expression.fieldName ? - this.fields.find(field => field.field === expressionItem.expression.fieldName) : null; - this.selectedCondition = expressionItem.expression.condition ? - expressionItem.expression.condition.name : null; - this.searchValue = expressionItem.expression.searchVal; - - expressionItem.inEditMode = true; - this._editedExpression = expressionItem; - - this.cdr.detectChanges(); - - this.entitySelectOverlaySettings.target = this.entitySelect.element; - this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; - this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); - this.fieldSelectOverlaySettings.target = this.fieldSelect.element; - this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; - this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); - this.conditionSelectOverlaySettings.target = this.conditionSelect.element; - this.conditionSelectOverlaySettings.excludeFromOutsideClick = [this.conditionSelect.element as HTMLElement]; - this.conditionSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); - - if (!this.selectedField) { - this.fieldSelect.input.nativeElement.focus(); - } else if (this.selectedField.filters.condition(this.selectedCondition).isUnary) { - this.conditionSelect.input.nativeElement.focus(); - } else { - const input = this.searchValueInput?.nativeElement || this.picker?.getEditElement(); - input.focus(); - } - } - - /** - * @hidden @internal - */ - public clearSelection() { - for (const group of this._selectedGroups) { - group.selected = false; - } - this._selectedGroups = []; - - for (const expr of this.selectedExpressions) { - expr.selected = false; - } - this.selectedExpressions = []; - - this.toggleContextMenu(); - } - - /** - * @hidden @internal - */ - public enterExpressionAdd(expressionItem: ExpressionOperandItem) { - this.clearSelection(); - this.exitOperandEdit(); - - if (this._addModeExpression) { - this._addModeExpression.inAddMode = false; - } - - expressionItem.inAddMode = true; - this._addModeExpression = expressionItem; - if (expressionItem.selected) { - this.toggleExpression(expressionItem); - } - } - - /** - * @hidden @internal - */ - public contextMenuClosed() { - this.contextualGroup = null; - } - - /** - * @hidden @internal - */ - public onKeyDown(eventArgs: KeyboardEvent) { - eventArgs.stopPropagation(); - const key = eventArgs.key; - if (!this.contextMenuToggle.collapsed && (key === this.platform.KEYMAP.ESCAPE)) { - this.clearSelection(); - } - } - - /** - * @hidden @internal - */ - public createAndGroup() { - this.createGroup(FilteringLogic.And); - } - - /** - * @hidden @internal - */ - public createOrGroup() { - this.createGroup(FilteringLogic.Or); - } - - /** - * @hidden @internal - */ - public deleteFilters() { - for (const expr of this.selectedExpressions) { - this.deleteItem(expr); - } - - this.clearSelection(); - } - - /** - * @hidden @internal - */ - public onGroupClick(groupItem: ExpressionGroupItem) { - this.toggleGroup(groupItem); - } - - /** - * @hidden @internal - */ - public ungroup() { - const selectedGroup = this.contextualGroup; - const parent = selectedGroup.parent; - if (parent) { - const index = parent.children.indexOf(selectedGroup); - parent.children.splice(index, 1, ...selectedGroup.children); - - for (const expr of selectedGroup.children) { - expr.parent = parent; - } - } - - this.clearSelection(); - this.commitOperandEdit(); - } - - /** - * @hidden @internal - */ - public deleteGroup() { - const selectedGroup = this.contextualGroup; - const parent = selectedGroup.parent; - if (parent) { - const index = parent.children.indexOf(selectedGroup); - parent.children.splice(index, 1); - } else { - this.rootGroup = null; - } - - this.clearSelection(); - this.commitOperandEdit(); - } - - /** - * @hidden @internal - */ - public selectFilteringLogic(event: IButtonGroupEventArgs) { - this.contextualGroup.operator = event.index as FilteringLogic; - this.commitOperandEdit(); - } - - /** - * @hidden @internal - */ - public getConditionFriendlyName(name: string): string { - return this.resourceStrings[`igx_query_builder_filter_${name}`] || name; - } - - /** - * @hidden @internal - */ - public isDate(value: any) { - return value instanceof Date; - } - - /** - * @hidden @internal - */ - public onExpressionsScrolled() { - if (!this.contextMenuToggle.collapsed) { - this.calculateContextMenuTarget(); - this.contextMenuToggle.reposition(); - } - } - - /** - * @hidden @internal - */ - public invokeClick(eventArgs: KeyboardEvent) { - if (this.platform.isActivationKey(eventArgs)) { - eventArgs.preventDefault(); - (eventArgs.currentTarget as HTMLElement).click(); - } - } - - /** - * @hidden @internal - */ - public openPicker(args: KeyboardEvent) { - if (this.platform.isActivationKey(args)) { - args.preventDefault(); - this.picker.open(); - } - } - - /** - * @hidden @internal - */ - public onOutletPointerDown(event) { - // This prevents closing the select's dropdown when clicking the scroll - event.preventDefault(); - } - - /** - * @hidden @internal - */ - public getConditionList(): string[] { - return this.selectedField ? this.selectedField.filters.conditionList() : []; - } - - /** - * @hidden @internal - */ - public getFormatter(field: string) { - return this.fields.find(el => el.field === field).formatter; - } - - /** - * @hidden @internal - */ - public getFormat(field: string) { - return this.fields.find(el => el.field === field).pipeArgs.format; + this.queryTree.exitOperandEdit(); } /** @@ -966,422 +205,7 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { * used by the grid */ public setAddButtonFocus() { - if (this.addRootAndGroupButton) { - this.addRootAndGroupButton.nativeElement.focus(); - } else if (this.addConditionButton) { - this.addConditionButton.nativeElement.focus(); - } - } - - /** - * @hidden @internal - */ - public context(expression: ExpressionItem, afterExpression?: ExpressionItem) { - return { - $implicit: expression, - afterExpression - }; - } - - /** - * @hidden @internal - */ - public onChipSelectionEnd() { - const contextualGroup = this.findSingleSelectedGroup(); - if (contextualGroup || this.selectedExpressions.length > 1) { - this.contextualGroup = contextualGroup; - this.calculateContextMenuTarget(); - if (this.contextMenuToggle.collapsed) { - this.contextMenuToggle.open(this._overlaySettings); - } else { - this.contextMenuToggle.reposition(); - } - } - } - - public onInnerQueryExpressionSearchTreeChange(expression: any, innerQuery: any) { - expression.searchTree = innerQuery.expressionTree; - this.expressionTreeChange.emit(); - } - - public formatReturnFields(returnFields: string | string[]) { - let text = returnFields; - if (Array.isArray(returnFields)) { - text = returnFields.join(', '); - text = text.length > 25 ? text.substring(0, 25) + ' ...' : text; - } - return text; - } - - private setFormat(field: FieldType) { - if (!field.pipeArgs) { - field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; - } - - if (!field.pipeArgs.format) { - field.pipeArgs.format = field.dataType === DataType.Time ? - DEFAULT_PIPE_TIME_FORMAT : field.dataType === DataType.DateTime ? - DEFAULT_PIPE_DATE_TIME_FORMAT : DEFAULT_PIPE_DATE_FORMAT; - } - - if (!field.defaultDateTimeFormat) { - field.defaultDateTimeFormat = DEFAULT_DATE_TIME_FORMAT; - } - - if (!field.defaultTimeFormat) { - field.defaultTimeFormat = DEFAULT_TIME_FORMAT; - } - } - - private setFilters(field: FieldType) { - if (!field.filters) { - switch (field.dataType) { - case DataType.Boolean: - field.filters = IgxBooleanFilteringOperand.instance(); - break; - case DataType.Number: - case DataType.Currency: - case DataType.Percent: - field.filters = IgxNumberFilteringOperand.instance(); - break; - case DataType.Date: - field.filters = IgxDateFilteringOperand.instance(); - break; - case DataType.Time: - field.filters = IgxTimeFilteringOperand.instance(); - break; - case DataType.DateTime: - field.filters = IgxDateTimeFilteringOperand.instance(); - break; - case DataType.String: - default: - field.filters = IgxStringFilteringOperand.instance(); - break; - } - - } - } - - private onToggleExpression(expressionItem: ExpressionOperandItem) { - this.exitOperandEdit(); - this.toggleExpression(expressionItem); - - this.toggleContextMenu(); - } - - private toggleExpression(expressionItem: ExpressionOperandItem) { - expressionItem.selected = !expressionItem.selected; - - if (expressionItem.selected) { - this.selectedExpressions.push(expressionItem); - } else { - const index = this.selectedExpressions.indexOf(expressionItem); - this.selectedExpressions.splice(index, 1); - this.deselectParentRecursive(expressionItem); - } - } - - private addGroup(operator: FilteringLogic, parent?: ExpressionGroupItem, afterExpression?: ExpressionItem) { - console.log('createGroup'); - this.cancelOperandAdd(); - - const groupItem = new ExpressionGroupItem(operator, parent); - - if (parent) { - if (afterExpression) { - const index = parent.children.indexOf(afterExpression); - parent.children.splice(index + 1, 0, groupItem); - } else { - parent.children.push(groupItem); - } - } else { - this.rootGroup = groupItem; - } - - this.addCondition(groupItem); - this.currentGroup = groupItem; - } - - private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem): ExpressionGroupItem { - let groupItem: ExpressionGroupItem; - if (expressionTree) { - groupItem = new ExpressionGroupItem(expressionTree.operator, parent); - - for (const expr of expressionTree.filteringOperands) { - if (expr instanceof FilteringExpressionsTree) { - groupItem.children.push(this.createExpressionGroupItem(expr, groupItem)); - } else { - const filteringExpr = expr as IFilteringExpression; - const exprCopy: IFilteringExpression = { - fieldName: filteringExpr.fieldName, - condition: filteringExpr.condition, - searchVal: filteringExpr.searchVal, - searchTree: filteringExpr.searchTree, // this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), - ignoreCase: filteringExpr.ignoreCase - }; - const operandItem = new ExpressionOperandItem(exprCopy, groupItem); - const entity = this.entities.find(el => el.name === expressionTree.entity); - if (entity) { - this.fields = entity.fields; - } - const field = this.fields.find(el => el.field === filteringExpr.fieldName); - operandItem.fieldLabel = field.label || field.header || field.field; - groupItem.children.push(operandItem); - this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); - this._selectedReturnFields = - !expressionTree.returnFields || expressionTree.returnFields === '*' || expressionTree.returnFields === 'All' - ? this.fields.map(f => f.field) - : this.fields.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); - } - } - } - return groupItem; - } - - private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem): FilteringExpressionsTree { - if (!groupItem) { - return null; - } - - const expressionTree = new FilteringExpressionsTree(groupItem.operator); - - for (const item of groupItem.children) { - if (item instanceof ExpressionGroupItem) { - const subTree = this.createExpressionTreeFromGroupItem((item as ExpressionGroupItem)); - expressionTree.filteringOperands.push(subTree); - } else { - expressionTree.filteringOperands.push((item as ExpressionOperandItem).expression); - } - } - - return expressionTree; - } - - private toggleContextMenu() { - const contextualGroup = this.findSingleSelectedGroup(); - - if (contextualGroup || this.selectedExpressions.length > 1) { - this.contextualGroup = contextualGroup; - - if (contextualGroup) { - this.filteringLogics = [ - { - label: this.resourceStrings.igx_query_builder_filter_operator_and, - selected: contextualGroup.operator === FilteringLogic.And - }, - { - label: this.resourceStrings.igx_query_builder_filter_operator_or, - selected: contextualGroup.operator === FilteringLogic.Or - } - ]; - } - } else if (this.contextMenuToggle) { - this.contextMenuToggle.close(); - } - } - - private findSingleSelectedGroup(): ExpressionGroupItem { - for (const group of this._selectedGroups) { - const containsAllSelectedExpressions = this.selectedExpressions.every(op => this.isInsideGroup(op, group)); - - if (containsAllSelectedExpressions) { - return group; - } - } - - return null; - } - - private isInsideGroup(item: ExpressionItem, group: ExpressionGroupItem): boolean { - if (!item) { - return false; - } - - if (item.parent === group) { - return true; - } - - return this.isInsideGroup(item.parent, group); - } - - private deleteItem(expressionItem: ExpressionItem) { - if (!expressionItem.parent) { - this.rootGroup = null; - this.currentGroup = null; - this._expressionTree = null; - return; - } - - if (expressionItem === this.currentGroup) { - this.currentGroup = this.currentGroup.parent; - } - - const children = expressionItem.parent.children; - const index = children.indexOf(expressionItem); - children.splice(index, 1); - const entity = this.expressionTree ? this.expressionTree.entity : null; - const returnFields = this.expressionTree ? this.expressionTree.returnFields : null; - this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); // TODO: don't recreate if not necessary - this._expressionTree.entity = entity; - this._expressionTree.returnFields = returnFields; - - if (!children.length) { - this.deleteItem(expressionItem.parent); - } - - this.expressionTreeChange.emit(); - } - - private createGroup(operator: FilteringLogic) { - const chips = this.chips.toArray(); - const minIndex = this.selectedExpressions.reduce((i, e) => Math.min(i, chips.findIndex(c => c.data === e)), Number.MAX_VALUE); - const firstExpression = chips[minIndex].data; - - const parent = firstExpression.parent; - const groupItem = new ExpressionGroupItem(operator, parent); - - const index = parent.children.indexOf(firstExpression); - parent.children.splice(index, 0, groupItem); - - for (const expr of this.selectedExpressions) { - groupItem.children.push(expr); - this.deleteItem(expr); - expr.parent = groupItem; - } - - this.clearSelection(); - } - - private toggleGroup(groupItem: ExpressionGroupItem) { - this.exitOperandEdit(); - if (groupItem.children && groupItem.children.length) { - this.toggleGroupRecursive(groupItem, !groupItem.selected); - if (!groupItem.selected) { - this.deselectParentRecursive(groupItem); - } - this.toggleContextMenu(); - } - } - - private toggleGroupRecursive(groupItem: ExpressionGroupItem, selected: boolean) { - if (groupItem.selected !== selected) { - groupItem.selected = selected; - - if (groupItem.selected) { - this._selectedGroups.push(groupItem); - } else { - const index = this._selectedGroups.indexOf(groupItem); - this._selectedGroups.splice(index, 1); - } - } - - for (const expr of groupItem.children) { - if (expr instanceof ExpressionGroupItem) { - this.toggleGroupRecursive(expr, selected); - } else { - const operandExpression = expr as ExpressionOperandItem; - if (operandExpression.selected !== selected) { - this.toggleExpression(operandExpression); - } - } - } - } - - private deselectParentRecursive(expressionItem: ExpressionItem) { - const parent = expressionItem.parent; - if (parent) { - if (parent.selected) { - parent.selected = false; - const index = this._selectedGroups.indexOf(parent); - this._selectedGroups.splice(index, 1); - } - this.deselectParentRecursive(parent); - } - } - - private calculateContextMenuTarget() { - const containerRect = this.expressionsContainer.nativeElement.getBoundingClientRect(); - const chips = this.chips.filter(c => this.selectedExpressions.indexOf(c.data) !== -1); - let minTop = chips.reduce((t, c) => - Math.min(t, c.nativeElement.getBoundingClientRect().top), Number.MAX_VALUE); - minTop = Math.max(containerRect.top, minTop); - minTop = Math.min(containerRect.bottom, minTop); - let maxRight = chips.reduce((r, c) => - Math.max(r, c.nativeElement.getBoundingClientRect().right), 0); - maxRight = Math.max(maxRight, containerRect.left); - maxRight = Math.min(maxRight, containerRect.right); - this._overlaySettings.target = new Point(maxRight, minTop); - } - - private scrollElementIntoView(target: HTMLElement) { - const container = this.expressionsContainer.nativeElement; - const targetOffset = target.offsetTop - container.offsetTop; - const delta = 10; - - if (container.scrollTop + delta > targetOffset) { - container.scrollTop = targetOffset - delta; - } else if (container.scrollTop + container.clientHeight < targetOffset + target.offsetHeight + delta) { - container.scrollTop = targetOffset + target.offsetHeight + delta - container.clientHeight; - } - } - - private init() { - this.clearSelection(); - this.cancelOperandAdd(); - this.cancelOperandEdit(); - this.rootGroup = this.createExpressionGroupItem(this.expressionTree); - this.currentGroup = this.rootGroup; - } - - private registerSVGIcons(): void { - const editorIcons = editor as any[]; - - editorIcons.forEach((icon) => { - this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons'); - this.iconService.addIconRef(icon.name, 'default', { - name: icon.name, - family: 'imx-icons' - }); - }); - - const inIcon = ''; - this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons'); - this.iconService.addIconRef('in', 'default', { - name: 'in', - family: 'imx-icons' - }); - - const notInIcon = ''; - this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons'); - this.iconService.addIconRef('not-in', 'default', { - name: 'not-in', - family: 'imx-icons' - }); - - this.iconService.addIconRef('add', 'default', { - name: 'add', - family: 'material', - }); - - this.iconService.addIconRef('close', 'default', { - name: 'close', - family: 'material', - }); - - this.iconService.addIconRef('check', 'default', { - name: 'check', - family: 'material', - }); - - this.iconService.addIconRef('delete', 'default', { - name: 'delete', - family: 'material', - }); - - this.iconService.addIconRef('edit', 'default', { - name: 'edit', - family: 'material', - }); + this.queryTree.setAddButtonFocus(); } } diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 3db2c5c4987..190f114744a 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -59,17 +59,25 @@ export class QueryBuilderComponent implements OnInit { searchVal: 'IC60', // ignoreCase: true }); + + const innerTree2 = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', 'Name'); + innerTree2.filteringOperands.push({ + fieldName: 'Name', + condition: IgxStringFilteringOperand.instance().condition('null'), + // ignoreCase: true + }); + const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', '*'); tree.filteringOperands.push({ fieldName: 'Id', condition: IgxStringFilteringOperand.instance().condition('in'), searchTree: innerTree }); - tree.filteringOperands.push({ - fieldName: 'Structure', - condition: IgxStringFilteringOperand.instance().condition('contains'), - searchVal: 'abc' - }); + // tree.filteringOperands.push({ + // fieldName: 'Structure', + // condition: IgxStringFilteringOperand.instance().condition('in'), + // searchTree: innerTree2 + // }); // const orTree = new FilteringExpressionsTree(FilteringLogic.Or); // orTree.filteringOperands.push({ From 030c9c72dc2454e3fe9c6346b08dba46a413e5f5 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 15 Aug 2024 16:43:18 +0300 Subject: [PATCH 014/147] feat(query-builder): go in edit mode from inner query and fixes --- .../advanced-filtering-dialog.component.html | 2 +- .../advanced-filtering-dialog.component.ts | 14 +++++++ .../query-builder-tree.component.html | 7 ++-- .../query-builder-tree.component.ts | 41 ++++++++++++++----- .../query-builder.component.html | 1 + .../query-builder/query-builder.component.ts | 5 +++ src/app/query-builder/query-builder.sample.ts | 7 ++++ 7 files changed, 62 insertions(+), 15 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html b/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html index 215cd1bc9fc..9d6946c6726 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html @@ -5,7 +5,7 @@ 'igx-advanced-filter--inline': inline }"> - +
>
-
+
style="margin: 10px 0px 10px 0px" [entities]="entities" [parentExpression]="expressionItem" - [expressionTree]="expressionItem.expression.searchTree"> + [expressionTree]="expressionItem.expression.searchTree" + (inEditModeChange)="onInEditModeChanged($event)"> diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 54240bcbfdc..86a1828a42a 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -222,7 +222,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * ``` */ @Output() - public expressionTreeChange = new EventEmitter(); + public expressionTreeChange = new EventEmitter(); + + @Output() + public inEditModeChange = new EventEmitter(); @ViewChild('entitySelect', { read: IgxSelectComponent }) protected entitySelect: IgxSelectComponent; @@ -488,10 +491,11 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (this._selectedReturnFields !== value && oldValue !== value) { this._selectedReturnFields = value; - // if (this._expressionTree) { - // this._expressionTree.returnFields = value.length === 0 ? '*' : value; - // // this.expressionTreeChange.emit(); - // } + + if (this._expressionTree) { + this._expressionTree.returnFields = value; + this.expressionTreeChange.emit(this._expressionTree); + } } } @@ -609,7 +613,11 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { : this.selectedField.field; const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0] - if (innerQuery && (this._editedExpression.expression.condition.name === 'in' || this._editedExpression.expression.condition.name === 'notIn')) { + if (innerQuery && (this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) { + if (!this._editedExpression.expression.searchTree) { + this._editedExpression.expression.searchTree = new FilteringExpressionsTree(innerQuery.expressionTree.operator); + } + this._editedExpression.expression.searchTree.entity = innerQuery.selectedEntity.name; this._editedExpression.expression.searchTree.returnFields = innerQuery.selectedReturnFields; this._editedExpression.expression.searchTree.filteringOperands = innerQuery.expressionTree.filteringOperands; @@ -624,7 +632,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); this._expressionTree.entity = this.selectedEntity.name; this._expressionTree.returnFields = this.selectedReturnFields; - this.expressionTreeChange.emit(); + this.expressionTreeChange.emit(this._expressionTree); } /** @@ -663,10 +671,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public operandCanBeCommitted(): boolean { - // TODO: fix when creating inner query + const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; return this.selectedField && this.selectedCondition && - (!!this.searchValue || !!this._editedExpression.expression.searchTree || this.selectedField.filters.condition(this.selectedCondition).isUnary); + ( + !!this.searchValue || + (innerQuery && !!innerQuery.expressionTree) || + this.selectedField.filters.condition(this.selectedCondition).isUnary + ); } /** @@ -733,6 +745,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._editedExpression.inEditMode = false; } + if (this.parentExpression) { + this.inEditModeChange.emit(this.parentExpression); + } + expressionItem.hovered = false; this.fields = this.selectedEntity ? this.selectedEntity.fields : null; this.selectedField = @@ -1017,7 +1033,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } public formatReturnFields(returnFields: string | string[]) { - let text = returnFields; if (Array.isArray(returnFields)) { text = returnFields.join(', '); @@ -1030,6 +1045,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { return !this.parentExpression || (this.parentExpression && this.parentExpression.inEditMode); } + public onInEditModeChanged(expressionItem: ExpressionOperandItem) { + this.enterExpressionEdit(expressionItem); + } + private setFormat(field: FieldType) { if (!field.pipeArgs) { field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; @@ -1245,7 +1264,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.deleteItem(expressionItem.parent); } - this.expressionTreeChange.emit(); + this.expressionTreeChange.emit(this._expressionTree); } private createGroup(operator: FilteringLogic) { diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index 2818b9109fa..b27b487eb03 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -11,5 +11,6 @@ [expressionTree]="this.expressionTree" [locale]="this.locale" [resourceStrings]="this.resourceStrings" + (expressionTreeChange)="onExpressionTreeChange($event)" > \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index a57ff957e17..cae47d862f2 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -207,5 +207,10 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { public setAddButtonFocus() { this.queryTree.setAddButtonFocus(); } + + public onExpressionTreeChange(tree: IExpressionTree) { + this.expressionTree = tree; + this.expressionTreeChange.emit(); + } } diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 190f114744a..919fa8ccc74 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -73,6 +73,13 @@ export class QueryBuilderComponent implements OnInit { condition: IgxStringFilteringOperand.instance().condition('in'), searchTree: innerTree }); + tree.filteringOperands.push({ + fieldName: 'Id', + condition: IgxStringFilteringOperand.instance().condition('equals'), + searchVal: '123', + ignoreCase: true + }); + // tree.filteringOperands.push({ // fieldName: 'Structure', // condition: IgxStringFilteringOperand.instance().condition('in'), From 07c801fd2dfb6a133140a46935ee3798e470345c Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 16 Aug 2024 10:16:35 +0300 Subject: [PATCH 015/147] feat(query-builder): fix creating inner query --- .../src/lib/query-builder/query-builder-tree.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 86a1828a42a..4c332144fbb 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1046,7 +1046,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } public onInEditModeChanged(expressionItem: ExpressionOperandItem) { - this.enterExpressionEdit(expressionItem); + if (!expressionItem.inEditMode) { + this.enterExpressionEdit(expressionItem); + } } private setFormat(field: FieldType) { From fb742d4aafa4aebe87c1e2a370ab8000d5ba3b0b Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 20 Aug 2024 10:29:16 +0300 Subject: [PATCH 016/147] feat(query-builder): add expand/collapse for inner queries --- .../src/i18n/BG/query-builder-resources.ts | 5 ++ .../src/i18n/CS/query-builder-resources.ts | 5 ++ .../src/i18n/DA/query-builder-resources.ts | 5 ++ .../src/i18n/DE/query-builder-resources.ts | 5 ++ .../src/i18n/ES/query-builder-resources.ts | 5 ++ .../src/i18n/FR/query-builder-resources.ts | 5 ++ .../src/i18n/HU/query-builder-resources.ts | 5 ++ .../src/i18n/IT/query-builder-resources.ts | 5 ++ .../src/i18n/JA/query-builder-resources.ts | 5 ++ .../src/i18n/KO/query-builder-resources.ts | 5 ++ .../src/i18n/NB/query-builder-resources.ts | 5 ++ .../src/i18n/NL/query-builder-resources.ts | 5 ++ .../src/i18n/PL/query-builder-resources.ts | 5 ++ .../src/i18n/PT/query-builder-resources.ts | 5 ++ .../src/i18n/RO/query-builder-resources.ts | 5 ++ .../src/i18n/SV/query-builder-resources.ts | 5 ++ .../src/i18n/TR/query-builder-resources.ts | 5 ++ .../i18n/ZH-HANS/query-builder-resources.ts | 5 ++ .../i18n/ZH-HANT/query-builder-resources.ts | 5 ++ .../lib/core/i18n/query-builder-resources.ts | 10 +++ .../query-builder-tree.component.html | 86 +++++++++---------- .../query-builder-tree.component.ts | 19 ++++ .../query-builder/query-builder.component.ts | 41 ++------- 23 files changed, 173 insertions(+), 78 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts index 8ce12dfe63a..161ca2d81dd 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts @@ -54,6 +54,11 @@ const QueryBuilderResourceStringsBG_: ExpandRequire [selected]="expressionItem.selected && isInEditMode()" (keydown)="invokeClick($event)" (click)="isInEditMode() ? onChipClick(expressionItem) : null" - (dblclick)="isInEditMode() ? onChipDblClick(expressionItem) : null" + (dblclick)="onChipDblClick(expressionItem)" (remove)="onChipRemove(expressionItem)" (selectedChanged)="onChipSelectionEnd()" > {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} - {{ getConditionFriendlyName( @@ -132,41 +125,47 @@
- -
+ + + - - - - -
- + +
+
+ {{this.resourceStrings.igx_query_builder_details}} + + +
+
[(ngModel)]="selectedEntity" [style.display]="isInEditMode() ? 'block' : 'none'" > - + {{entity.name}} @@ -427,10 +427,10 @@
[displayKey]="'field'" [valueKey]="'field'" [(ngModel)]="selectedReturnFields" - searchPlaceholder="Search..." + searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" > - +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 4c332144fbb..afe44018806 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -66,6 +66,7 @@ export class IgxFieldFormatterPipe implements PipeTransform { class ExpressionItem { public parent: ExpressionGroupItem; public selected: boolean; + public expanded: boolean; constructor(parent?: ExpressionGroupItem) { this.parent = parent; } @@ -1032,6 +1033,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } + /** + * @hidden + */ + public expanderClick(event, expressionItem: ExpressionOperandItem) { + expressionItem.expanded = !expressionItem.expanded; + event.stopPropagation(); + } + public formatReturnFields(returnFields: string | string[]) { let text = returnFields; if (Array.isArray(returnFields)) { @@ -1419,6 +1428,16 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { name: 'edit', family: 'material', }); + + this.iconService.addIconRef('unfold_less', 'default', { + name: 'unfold_less', + family: 'material', + }); + + this.iconService.addIconRef('unfold_more', 'default', { + name: 'unfold_more', + family: 'material', + }); } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index cae47d862f2..dd3d2d2e0df 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -1,44 +1,15 @@ -import { AfterViewInit, ContentChild, EventEmitter, LOCALE_ID, Output, Pipe, PipeTransform } from '@angular/core'; -import { getLocaleFirstDayOfWeek, NgIf, NgFor, NgTemplateOutlet, NgClass, DatePipe } from '@angular/common'; -import { Inject } from '@angular/core'; +import { AfterViewInit, ContentChild, EventEmitter, Output } from '@angular/core'; +import { NgIf} from '@angular/common'; import { - Component, Input, ViewChild, ChangeDetectorRef, ViewChildren, QueryList, ElementRef, OnDestroy, HostBinding + Component, Input, ViewChild, ElementRef, OnDestroy, HostBinding } from '@angular/core'; -import { FormsModule } from '@angular/forms'; import { Subject } from 'rxjs'; -import { editor } from '@igniteui/material-icons-extended'; -import { IButtonGroupEventArgs, IgxButtonGroupComponent } from '../buttonGroup/buttonGroup.component'; -import { IgxChipComponent } from '../chips/chip.component'; import { IQueryBuilderResourceStrings, QueryBuilderResourceStringsEN } from '../core/i18n/query-builder-resources'; -import { PlatformUtil } from '../core/utils'; -import { DataType, DataUtil } from '../data-operations/data-util'; -import { IgxBooleanFilteringOperand, IgxDateFilteringOperand, IgxDateTimeFilteringOperand, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxTimeFilteringOperand } from '../data-operations/filtering-condition'; -import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface'; -import { FilteringExpressionsTree, IExpressionTree } from '../data-operations/filtering-expressions-tree'; -import { IgxDatePickerComponent } from '../date-picker/date-picker.component'; - -import { IgxButtonDirective } from '../directives/button/button.directive'; -import { IgxDateTimeEditorDirective } from '../directives/date-time-editor/date-time-editor.directive'; - -import { IgxOverlayOutletDirective, IgxToggleActionDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive'; -import { FieldType, EntityType } from '../grids/common/grid.interface'; -import { IgxIconService } from '../icon/icon.service'; -import { IgxSelectComponent } from '../select/select.component'; -import { HorizontalAlignment, OverlaySettings, Point, VerticalAlignment } from '../services/overlay/utilities'; -import { AbsoluteScrollStrategy, AutoPositionStrategy, CloseScrollStrategy, ConnectedPositioningStrategy } from '../services/public_api'; -import { IgxTimePickerComponent } from '../time-picker/time-picker.component'; +import { IExpressionTree } from '../data-operations/filtering-expressions-tree'; +import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; +import { EntityType } from '../grids/common/grid.interface'; import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component'; -import { IgxPickerToggleComponent, IgxPickerClearComponent } from '../date-common/picker-icons.common'; -import { IgxInputDirective } from '../directives/input/input.directive'; -import { IgxInputGroupComponent } from '../input-group/input-group.component'; -import { IgxSelectItemComponent } from '../select/select-item.component'; -import { IgxSuffixDirective } from '../directives/suffix/suffix.directive'; -import { IgxPrefixDirective } from '../directives/prefix/prefix.directive'; -import { IgxIconComponent } from '../icon/icon.component'; import { getCurrentResourceStrings } from '../core/i18n/resources'; -import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; -import { IgxComboComponent } from "../combo/combo.component"; -import { IgxLabelDirective } from '../input-group/public_api'; import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component'; /** From 43928a91cd2427b9b8ed819e8026aa975efcd1b3 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 21 Aug 2024 11:03:09 +0300 Subject: [PATCH 017/147] feat(query-builder): restore expanded and add select all in return fields --- .../src/i18n/BG/query-builder-resources.ts | 1 + .../src/i18n/CS/query-builder-resources.ts | 1 + .../src/i18n/DA/query-builder-resources.ts | 1 + .../src/i18n/DE/query-builder-resources.ts | 1 + .../src/i18n/ES/query-builder-resources.ts | 1 + .../src/i18n/FR/query-builder-resources.ts | 1 + .../src/i18n/HU/query-builder-resources.ts | 1 + .../src/i18n/IT/query-builder-resources.ts | 1 + .../src/i18n/JA/query-builder-resources.ts | 1 + .../src/i18n/KO/query-builder-resources.ts | 1 + .../src/i18n/NB/query-builder-resources.ts | 1 + .../src/i18n/NL/query-builder-resources.ts | 1 + .../src/i18n/PL/query-builder-resources.ts | 1 + .../src/i18n/PT/query-builder-resources.ts | 1 + .../src/i18n/RO/query-builder-resources.ts | 1 + .../src/i18n/SV/query-builder-resources.ts | 1 + .../src/i18n/TR/query-builder-resources.ts | 1 + .../i18n/ZH-HANS/query-builder-resources.ts | 1 + .../i18n/ZH-HANT/query-builder-resources.ts | 1 + .../lib/core/i18n/query-builder-resources.ts | 2 + .../query-builder-tree.component.html | 16 ++++++- .../query-builder-tree.component.ts | 42 ++++++++++++++++--- 22 files changed, 73 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts index 161ca2d81dd..dc5a184e613 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts @@ -54,6 +54,7 @@ const QueryBuilderResourceStringsBG_: ExpandRequire *ngIf="!expressionItem.expression.condition.isUnary" > - {{expressionItem.expression.searchTree.entity}} / {{formatReturnFields(expressionItem.expression.searchTree.returnFields)}} + {{expressionItem.expression.searchTree.entity}} / {{formatReturnFields(expressionItem.expression.searchTree)}} @@ -421,6 +421,7 @@
searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" > + + + + {{ this.resourceStrings.igx_query_builder_select_all }} + diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index afe44018806..69b753fdfa3 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -14,7 +14,7 @@ import { PlatformUtil } from '../core/utils'; import { DataType, DataUtil } from '../data-operations/data-util'; import { IgxBooleanFilteringOperand, IgxDateFilteringOperand, IgxDateTimeFilteringOperand, IgxNumberFilteringOperand, IgxStringFilteringOperand, IgxTimeFilteringOperand } from '../data-operations/filtering-condition'; import { FilteringLogic, IFilteringExpression } from '../data-operations/filtering-expression.interface'; -import { FilteringExpressionsTree, IExpressionTree } from '../data-operations/filtering-expressions-tree'; +import { FilteringExpressionsTree, IExpressionTree, IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree'; import { IgxDatePickerComponent } from '../date-picker/date-picker.component'; import { IgxButtonDirective } from '../directives/button/button.directive'; @@ -39,6 +39,8 @@ import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxIconButtonDirective } from '../directives/button/icon-button.directive'; import { IgxComboComponent } from "../combo/combo.component"; import { IgxLabelDirective } from '../input-group/public_api'; +import { IgxComboHeaderDirective } from '../combo/public_api'; +import { IgxCheckboxComponent } from "../checkbox/checkbox.component"; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; @@ -108,7 +110,7 @@ class ExpressionOperandItem extends ExpressionItem { selector: 'igx-query-builder-tree', templateUrl: './query-builder-tree.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective, IgxComboHeaderDirective, IgxCheckboxComponent] }) export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** @@ -231,6 +233,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @ViewChild('entitySelect', { read: IgxSelectComponent }) protected entitySelect: IgxSelectComponent; + @ViewChild('selectedReturnFieldsCombo', { read: IgxComboComponent }) + private selectedReturnFieldsCombo: IgxComboComponent; + @ViewChild('fieldSelect', { read: IgxSelectComponent }) private fieldSelect: IgxSelectComponent; @@ -403,6 +408,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { private _addModeExpression: ExpressionOperandItem; private _editedExpression: ExpressionOperandItem; private _selectedGroups: ExpressionGroupItem[] = []; + private _expandedExpressions: IFilteringExpression[] = []; private _fields: FieldType[]; private _expressionTree: IExpressionTree; private _locale; @@ -1038,14 +1044,26 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public expanderClick(event, expressionItem: ExpressionOperandItem) { expressionItem.expanded = !expressionItem.expanded; + if (expressionItem.expanded) { + this._expandedExpressions.push(expressionItem.expression); + } else { + const matchIndex = this._expandedExpressions.indexOf(expressionItem.expression); + this._expandedExpressions.splice(matchIndex, 1); + } event.stopPropagation(); } - public formatReturnFields(returnFields: string | string[]) { + public formatReturnFields(innerTree: IFilteringExpressionsTree) { + const returnFields = innerTree.returnFields; let text = returnFields; if (Array.isArray(returnFields)) { - text = returnFields.join(', '); - text = text.length > 25 ? text.substring(0, 25) + ' ...' : text; + const innerTreeEntity = this.entities.find(el => el.name === innerTree.entity); + if (returnFields.length === innerTreeEntity.fields.length) { + text = this.resourceStrings.igx_query_builder_all_fields; + } else { + text = returnFields.join(', '); + text = text.length > 25 ? text.substring(0, 25) + ' ...' : text; + } } return text; } @@ -1060,6 +1078,17 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } + public onSelectAllClicked(event) { + if ( + (this._selectedReturnFields.length > 0 && this._selectedReturnFields.length < this._selectedEntity.fields.length) || + this._selectedReturnFields.length == this._selectedEntity.fields.length + ) { + this.selectedReturnFieldsCombo.deselectAllItems(); + } else { + this.selectedReturnFieldsCombo.selectAllItems(); + } + } + private setFormat(field: FieldType) { if (!field.pipeArgs) { field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; @@ -1172,6 +1201,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } const field = this.fields.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field.label || field.header || field.field; + if (this._expandedExpressions.filter(e => e.searchTree == operandItem.expression.searchTree).length > 0) { + operandItem.expanded = true; + } groupItem.children.push(operandItem); this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); this._selectedReturnFields = From e52210dcf69672c075df313d56f2abc72b2624e9 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Tue, 20 Aug 2024 15:09:04 +0300 Subject: [PATCH 018/147] feat(query-builder): Modified input model to reflect specification --- .../src/lib/data-operations/data-util.spec.ts | 12 +- .../filtering-expression.interface.ts | 8 +- .../filtering-expressions-tree.ts | 16 +- .../filtering-strategy.spec.ts | 10 +- .../lib/data-operations/filtering-strategy.ts | 4 +- .../base/grid-filtering-row.component.ts | 3 +- .../excel-style-custom-dialog.component.ts | 12 +- .../excel-style-filtering.component.ts | 8 +- .../excel-style-search.component.ts | 9 +- .../grids/filtering/grid-filtering.service.ts | 8 +- .../src/lib/grids/grid-base.directive.ts | 2 +- .../grid/grid-filtering-advanced.spec.ts | 158 +++++++++--------- .../lib/grids/grid/grid-filtering-ui.spec.ts | 122 +++++++------- .../src/lib/grids/grid/grid-filtering.spec.ts | 12 +- .../lib/grids/grid/grid-row-selection.spec.ts | 4 +- .../src/lib/grids/grid/grid.component.spec.ts | 2 +- .../lib/grids/pivot-grid/pivot-grid.spec.ts | 6 +- .../src/lib/grids/state-base.directive.ts | 12 +- .../src/lib/grids/state.directive.spec.ts | 4 +- .../lib/grids/state.hierarchicalgrid.spec.ts | 4 +- .../src/lib/grids/state.treegrid.spec.ts | 4 +- .../grids/summaries/grid-summary.service.ts | 2 +- ...id-toolbar-advanced-filtering.component.ts | 2 +- .../tree-grid/tree-grid-selection.spec.ts | 24 +-- .../query-builder-tree.component.ts | 35 ++-- .../services/csv/csv-exporter-grid.spec.ts | 14 +- .../excel/excel-exporter-grid.spec.ts | 10 +- .../src/lib/test-utils/grid-samples.spec.ts | 4 +- .../grid-external-filtering.sample.ts | 12 +- .../grid-filtering/grid-filtering.sample.ts | 14 +- src/app/pivot-grid/pivot-grid.sample.ts | 3 +- src/app/query-builder/query-builder.sample.ts | 23 ++- 32 files changed, 296 insertions(+), 267 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts index e28623afcc9..a2104839e2d 100644 --- a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts @@ -350,7 +350,7 @@ const testFilter = () => { }; state.expressionsTree.filteringOperands = [ { - fieldName: 'number', + field: 'number', condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), searchVal: 3 } @@ -367,7 +367,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - fieldName: 'string', + field: 'string', searchVal: 'row' } ]; @@ -378,7 +378,7 @@ const testFilter = () => { stateIgnoreCase.expressionsTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - fieldName: 'string', + field: 'string', ignoreCase: false, searchVal: 'ROW' } @@ -401,7 +401,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxDateFilteringOperand.instance().condition('after'), - fieldName: 'date', + field: 'date', searchVal: new Date() } ]; @@ -416,7 +416,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxBooleanFilteringOperand.instance().condition('false'), - fieldName: 'boolean' + field: 'boolean' } ]; const res = FilterUtil.filter(data, state); @@ -431,7 +431,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxBooleanFilteringOperand.instance().condition('false'), - fieldName: 'boolean' + field: 'boolean' } ]; const res = FilterUtil.filter(data, state); diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index 07362251b12..3770b6f8a76 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -1,5 +1,6 @@ import { IExpressionTree } from 'igniteui-angular'; import { IFilteringOperation } from './filtering-condition'; +import { Serializable } from 'node:child_process'; /* mustCoerceToInt */ export enum FilteringLogic { @@ -13,9 +14,10 @@ export enum FilteringLogic { * Represents filtering expressions. */ export declare interface IFilteringExpression { - fieldName: string; - condition: IFilteringOperation; - searchVal?: any; + field: string; + condition?: IFilteringOperation; + conditionName: string; + searchVal?: Serializable; searchTree?: IExpressionTree; ignoreCase?: boolean; } diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts index 80a8496639a..7aecdd01ca2 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts @@ -10,9 +10,9 @@ export enum FilteringExpressionsTreeType { export declare interface IExpressionTree { filteringOperands: (IExpressionTree | IFilteringExpression)[]; operator: FilteringLogic; - fieldName?: string; + field?: string; entity?: string; - returnFields?: string | string[]; + returnFields?: string[]; } /* marshalByValue */ @@ -75,7 +75,7 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { * * @memberof FilteringExpressionsTree */ - public fieldName?: string; + public field?: string; /* alternateName: treeType */ /** @@ -93,9 +93,9 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { public entity?: string; - public returnFields?: string | string[]; + public returnFields?: string[]; - constructor(operator: FilteringLogic, entity?: string, returnFields?: string | string[]) { + constructor(operator: FilteringLogic, entity?: string, returnFields?: string[]) { this.operator = operator; this.entity = entity; this.returnFields = returnFields; @@ -147,7 +147,7 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { return i; } } else { - if ((expr as IFilteringExpression).fieldName === fieldName) { + if ((expr as IFilteringExpression).field === fieldName) { return i; } } @@ -157,14 +157,14 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { } protected isFilteringExpressionsTreeForColumn(expressionsTree: IFilteringExpressionsTree, fieldName: string): boolean { - if (expressionsTree.fieldName === fieldName) { + if (expressionsTree.field === fieldName) { return true; } for (const expr of expressionsTree.filteringOperands) { if ((expr instanceof FilteringExpressionsTree)) { return this.isFilteringExpressionsTreeForColumn(expr, fieldName); - } else if ((expr as IFilteringExpression).fieldName === fieldName) { + } else if ((expr as IFilteringExpression).field === fieldName) { return true; } } diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts index 22cf2f0b03e..7ebc7ca7791 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts @@ -19,7 +19,7 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - fieldName: 'number', + field: 'number', searchVal: 1 } ]; @@ -33,13 +33,13 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - fieldName: 'string', + field: 'string', ignoreCase: false, searchVal: 'ROW' }, { condition: IgxNumberFilteringOperand.instance().condition('lessThan'), - fieldName: 'number', + field: 'number', searchVal: 1 } ]; @@ -50,7 +50,7 @@ describe('Unit testing FilteringStrategy', () => { const rec = data[0]; const res = fs.findMatchByExpression(rec, { condition: IgxBooleanFilteringOperand.instance().condition('false'), - fieldName: 'boolean' + field: 'boolean' }); expect(res).toBeTruthy(); }); @@ -61,7 +61,7 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - fieldName: 'string', + field: 'string', searchVal: 'ROW' } ]; diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts index 9674032ee56..7ee6c3b4cfb 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -39,7 +39,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { // protected public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean { const cond = expr.condition; - const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid); + const val = this.getFieldValue(rec, expr.field, isDate, isTime, grid); return cond.logic(val, expr.searchVal, expr.ignoreCase); } @@ -72,7 +72,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } else { const expression = expressions as IFilteringExpression; - const column = grid && grid.getColumnByName(expression.fieldName); + const column = grid && grid.getColumnByName(expression.field); const isDate = column ? column.dataType === DateType || column.dataType === DateTimeType : false; const isTime = column ? column.dataType === TimeType : false; return this.findMatchByExpression(rec, expression, isDate, isTime, grid); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts index cf13b18f033..18b426abe02 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts @@ -847,8 +847,9 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy { private resetExpression(condition?: string) { this.expression = { - fieldName: this.column.field, + field: this.column.field, condition: null, + conditionName: null, searchVal: null, ignoreCase: this.column.filteringIgnoreCase }; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts index eab8f8dc4fe..9c573e23c5e 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts @@ -167,7 +167,8 @@ export class IgxExcelStyleCustomDialogComponent implements AfterViewInit { const exprUI = new ExpressionUI(); exprUI.expression = { condition: null, - fieldName: this.column.field, + conditionName: null, + field: this.column.field, ignoreCase: this.column.filteringIgnoreCase, searchVal: null }; @@ -250,9 +251,11 @@ export class IgxExcelStyleCustomDialogComponent implements AfterViewInit { firstExprUI = this.expressionsList.pop(); } else { this.expressionsList = []; + const cond = this.createCondition(this.selectedOperator); firstExprUI.expression = { - condition: this.createCondition(this.selectedOperator), - fieldName: this.column.field, + condition: cond, + conditionName: cond.name, + field: this.column.field, ignoreCase: this.column.filteringIgnoreCase, searchVal: null }; @@ -264,7 +267,8 @@ export class IgxExcelStyleCustomDialogComponent implements AfterViewInit { const secondExprUI = new ExpressionUI(); secondExprUI.expression = { condition: null, - fieldName: this.column.field, + conditionName: null, + field: this.column.field, ignoreCase: this.column.filteringIgnoreCase, searchVal: null }; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts index ae00ecde7db..19ded5791b0 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts @@ -569,7 +569,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return [...arr, ...Array.from((e.expression.searchVal as Set).values()).map(v => new Date(v).toISOString())]; } - return [...arr, ...[e.expression.searchVal ? e.expression.searchVal.toISOString() : e.expression.searchVal]]; + return [...arr, ...[e.expression.searchVal ? (e.expression.searchVal as any).toISOString() : e.expression.searchVal]]; }, [])); } else if (this.column.dataType === GridColumnDataType.Time) { filterValues = new Set(this.expressionsList.reduce((arr, e) => { @@ -577,7 +577,7 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent return [...arr, ...Array.from((e.expression.searchVal as Set).values()).map(v => typeof v === 'string' ? v : new Date(v).toLocaleTimeString())]; } - return [...arr, ...[e.expression.searchVal ? e.expression.searchVal.toLocaleTimeString() : e.expression.searchVal]]; + return [...arr, ...[e.expression.searchVal ? (e.expression.searchVal as any).toLocaleTimeString() : e.expression.searchVal]]; }, [])); } else { filterValues = new Set(this.expressionsList.reduce((arr, e) => { @@ -619,12 +619,12 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private getColumnFilterExpressionsTree() { const gridExpressionsTree: IFilteringExpressionsTree = this.grid.filteringExpressionsTree; - const expressionsTree = new FilteringExpressionsTree(gridExpressionsTree.operator, gridExpressionsTree.fieldName); + const expressionsTree = new FilteringExpressionsTree(gridExpressionsTree.operator, gridExpressionsTree.field); for (const operand of gridExpressionsTree.filteringOperands) { if (operand instanceof FilteringExpressionsTree) { const columnExprTree = operand as FilteringExpressionsTree; - if (columnExprTree.fieldName === this.column.field) { + if (columnExprTree.field === this.column.field) { continue; } } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts index 2c1a850a32c..4a487872124 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts @@ -569,7 +569,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } filterTree.filteringOperands.push({ condition, - fieldName: this.esf.column.field, + conditionName: condition.name, + field: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, searchVal: element.value }); @@ -583,7 +584,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { } filterTree.filteringOperands.push({ condition: this.createCondition('in'), - fieldName: this.esf.column.field, + conditionName: 'in', + field: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, searchVal: new Set(this.esf.column.dataType === GridColumnDataType.Date || this.esf.column.dataType === GridColumnDataType.DateTime ? @@ -595,7 +597,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { if (blanksItem) { filterTree.filteringOperands.push({ condition: this.createCondition('empty'), - fieldName: this.esf.column.field, + conditionName: 'empty', + field: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, searchVal: blanksItem.value }); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts index 0561e352a10..6e2d80e78c7 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts @@ -293,7 +293,7 @@ export class IgxFilteringService implements OnDestroy { } const filteringTree = this.grid.filteringExpressionsTree; - const newFilteringTree = new FilteringExpressionsTree(filteringTree.operator, filteringTree.fieldName); + const newFilteringTree = new FilteringExpressionsTree(filteringTree.operator, filteringTree.field); for (const column of this.grid.columns) { this.prepare_filtering_expression(newFilteringTree, column.field, value, condition, @@ -463,7 +463,7 @@ export class IgxFilteringService implements OnDestroy { if (expression.condition.isUnary) { return this.grid.resourceStrings[`igx_grid_filter_${expression.condition.name}`] || expression.condition.name; } else if (expression.searchVal instanceof Date) { - const column = this.grid.getColumnByName(expression.fieldName); + const column = this.grid.getColumnByName(expression.field); const formatter = column.formatter; if (formatter) { return formatter(expression.searchVal, undefined); @@ -541,12 +541,12 @@ export class IgxFilteringService implements OnDestroy { let newExpressionsTree = filteringState as FilteringExpressionsTree; if (createNewTree) { - newExpressionsTree = new FilteringExpressionsTree(filteringState.operator, filteringState.fieldName); + newExpressionsTree = new FilteringExpressionsTree(filteringState.operator, filteringState.field); newExpressionsTree.filteringOperands = [...filteringState.filteringOperands]; } if (condition) { - const newExpression: IFilteringExpression = { fieldName, searchVal, condition, ignoreCase }; + const newExpression: IFilteringExpression = { field: fieldName, searchVal, condition, conditionName: condition.name, ignoreCase }; expressionsTree = new FilteringExpressionsTree(filteringState.operator, fieldName); expressionsTree.filteringOperands.push(newExpression); } diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 38f0d292a6b..4296e6b2f88 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -1841,7 +1841,7 @@ export abstract class IgxGridBaseDirective implements GridType, const val = (value as FilteringExpressionsTree); for (let index = 0; index < val.filteringOperands.length; index++) { if (!(val.filteringOperands[index] instanceof FilteringExpressionsTree)) { - const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, val.filteringOperands[index].fieldName); + const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, val.filteringOperands[index].field); newExpressionsTree.filteringOperands.push(val.filteringOperands[index] as IFilteringExpression); val.filteringOperands[index] = newExpressionsTree; } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index ca69837bb04..7a1a857a628 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -797,15 +797,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -842,15 +842,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1200,7 +1200,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); @@ -1236,7 +1236,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') }); } grid.advancedFilteringExpressionsTree = tree; @@ -1274,7 +1274,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') }); } grid.advancedFilteringExpressionsTree = tree; @@ -1313,7 +1313,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') }); } grid.advancedFilteringExpressionsTree = tree; @@ -1347,7 +1347,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 20; index++) { tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') }); } grid.advancedFilteringExpressionsTree = tree; @@ -1382,15 +1382,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1420,15 +1420,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1483,15 +1483,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1563,11 +1563,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1610,11 +1610,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1662,11 +1662,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1736,15 +1736,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1786,11 +1786,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -1820,11 +1820,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -1857,24 +1857,24 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); const andTree = new FilteringExpressionsTree(FilteringLogic.Or); andTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); andTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push(andTree); @@ -2074,15 +2074,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2120,15 +2120,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2184,15 +2184,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2248,11 +2248,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2305,15 +2305,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2362,15 +2362,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2406,15 +2406,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2462,15 +2462,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2528,15 +2528,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2606,7 +2606,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); @@ -2631,15 +2631,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2707,11 +2707,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2744,11 +2744,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2778,11 +2778,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2812,11 +2812,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2849,15 +2849,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push(orTree); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index d85f174cc07..3fab8cdeb6b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -1039,8 +1039,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1358,9 +1358,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1384,9 +1384,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1442,9 +1442,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1641,9 +1641,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1675,9 +1675,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1713,9 +1713,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1750,8 +1750,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1841,10 +1841,10 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2333,11 +2333,11 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'x', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'y', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'x', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'y', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2409,8 +2409,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2497,8 +2497,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 25, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, - { fieldName: 'Downloads', searchVal: 200, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { field: 'Downloads', searchVal: 25, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, + { field: 'Downloads', searchVal: 200, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2755,12 +2755,12 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { it('Should not prevent mousedown event when target is within the filter cell template', fakeAsync(() => { const filterCell = GridFunctions.getFilterCell(fix, 'ProductName'); const input = filterCell.query(By.css('input')).nativeElement; - + const mousedownEvent = new MouseEvent('mousedown', { bubbles: true }); const preventDefaultSpy = spyOn(mousedownEvent, 'preventDefault'); input.dispatchEvent(mousedownEvent, { bubbles: true }); fix.detectChanges(); - + expect(preventDefaultSpy).not.toHaveBeenCalled(); })); @@ -2772,7 +2772,7 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const preventDefaultSpy = spyOn(mousedownEvent, 'preventDefault'); firstCell.dispatchEvent(mousedownEvent); fix.detectChanges(); - + expect(preventDefaultSpy).toHaveBeenCalled(); })); }); @@ -3331,8 +3331,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3351,7 +3351,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3385,7 +3385,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3419,8 +3419,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3448,8 +3448,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3474,8 +3474,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3507,8 +3507,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3563,8 +3563,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3607,8 +3607,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3626,8 +3626,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3654,8 +3654,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { fieldName: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, + { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3753,8 +3753,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -6994,7 +6994,7 @@ const verifyGridSubmenuSize = (gridNativeElement: HTMLElement, expectedSize: Siz }; const verifyFilteringExpression = (operand: IFilteringExpression, fieldName: string, conditionName: string, searchVal: any) => { - expect(operand.fieldName).toBe(fieldName); + expect(operand.field).toBe(fieldName); expect(operand.condition.name).toBe(conditionName); expect(operand.searchVal).toEqual(searchVal); }; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts index fdd9c9e2495..950416f1382 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts @@ -770,8 +770,8 @@ describe('IgxGrid - Filtering actions #grid', () => { const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); gridExpressionsTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, - { fieldName: 'ID', searchVal: 4, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') } + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, + { field: 'ID', searchVal: 4, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') } ]; grid.filteringExpressionsTree = gridExpressionsTree; @@ -839,14 +839,14 @@ describe('IgxGrid - Filtering actions #grid', () => { it('Should correctly apply two conditions to two columns at once.', fakeAsync(() => { const colDownloadsExprTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); colDownloadsExprTree.filteringOperands = [ - { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, - { fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('lessThanOrEqualTo') } + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, + { field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('lessThanOrEqualTo') } ]; const colIdExprTree = new FilteringExpressionsTree(FilteringLogic.And, 'ID'); colIdExprTree.filteringOperands = [ - { fieldName: 'ID', searchVal: 1, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, - { fieldName: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { field: 'ID', searchVal: 1, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, + { field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } ]; const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts index 976c0d13f54..44be1015ce8 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts @@ -2013,12 +2013,12 @@ describe('IgxGrid - Row Selection #grid', () => { grid.height = '1100px'; const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'UnitsInStock', + field: 'UnitsInStock', searchVal: 0, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), }); tree.filteringOperands.push({ - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 7bf016fd4c6..21109a6b21e 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -563,7 +563,7 @@ describe('IgxGrid Component Tests #grid', () => { grid.filteringExpressionsTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('equals'), - fieldName: 'index', + field: 'index', searchVal: 0 } ]; diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts index ff843efc268..273b5d1e2e5 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts @@ -227,7 +227,7 @@ describe('IgxPivotGrid #pivotGrid', () => { filteringExpressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('equals'), - fieldName: 'SellerName', + field: 'SellerName', searchVal: 'Stanley' } ]; @@ -2731,7 +2731,7 @@ describe('IgxPivotGrid #pivotGrid', () => { filterColumnExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('in'), - fieldName: 'City', + field: 'City', searchVal: new Set(['Ciudad de la Costa']) } ]; @@ -2739,7 +2739,7 @@ describe('IgxPivotGrid #pivotGrid', () => { filterRowExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('in'), - fieldName: 'ProductCategory', + field: 'ProductCategory', searchVal: new Set(['Bikes']) } ]; diff --git a/projects/igniteui-angular/src/lib/grids/state-base.directive.ts b/projects/igniteui-angular/src/lib/grids/state-base.directive.ts index 4a5923e9dfa..4c15d29306a 100644 --- a/projects/igniteui-angular/src/lib/grids/state-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/state-base.directive.ts @@ -642,7 +642,7 @@ export class IgxGridStateBaseDirective { return null; } - const expressionsTree = new FilteringExpressionsTree(exprTreeObject.operator, exprTreeObject.fieldName); + const expressionsTree = new FilteringExpressionsTree(exprTreeObject.operator, exprTreeObject.field); for (const item of exprTreeObject.filteringOperands) { // Check if item is an expressions tree or a single expression. @@ -653,11 +653,11 @@ export class IgxGridStateBaseDirective { const expr = item as IFilteringExpression; let dataType: string; if (this.currGrid instanceof IgxPivotGridComponent) { - dataType = this.currGrid.allDimensions.find(x => x.memberName === expr.fieldName).dataType; + dataType = this.currGrid.allDimensions.find(x => x.memberName === expr.field).dataType; } else if (this.currGrid.columns.length > 0) { - dataType = this.currGrid.columns.find(c => c.field === expr.fieldName).dataType; + dataType = this.currGrid.columns.find(c => c.field === expr.field).dataType; } else if (this.state.columns) { - dataType = this.state.columns.find(c => c.field === expr.fieldName).dataType; + dataType = this.state.columns.find(c => c.field === expr.field).dataType; } else { return null; } @@ -667,11 +667,11 @@ export class IgxGridStateBaseDirective { if (Array.isArray(expr.searchVal)) { expr.searchVal = new Set(expr.searchVal); } else { - expr.searchVal = expr.searchVal && (dataType === 'date' || dataType === 'dateTime') ? new Date(Date.parse(expr.searchVal)) : expr.searchVal; + expr.searchVal = expr.searchVal && (dataType === 'date' || dataType === 'dateTime') ? new Date(Date.parse(expr.searchVal.toString())) : expr.searchVal; } const condition = this.generateFilteringCondition(dataType, expr.condition.name) || - this.currGrid.columns.find(c => c.field === expr.fieldName).filters.condition(expr.condition.name); + this.currGrid.columns.find(c => c.field === expr.field).filters.condition(expr.condition.name); if (condition) { expr.condition = condition; diff --git a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts index ebc79984a73..292fb7741c0 100644 --- a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts @@ -749,7 +749,7 @@ class HelperFunctions { } public static verifyFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { - expect(expressions.fieldName).toBe(gridState.filtering.fieldName, 'Filtering expression field name is not correct'); + expect(expressions.field).toBe(gridState.filtering.field, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.filtering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.filtering.filteringOperands[i])); @@ -758,7 +758,7 @@ class HelperFunctions { public static verifyAdvancedFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { if (gridState.advancedFiltering) { - expect(expressions.fieldName).toBe(gridState.advancedFiltering.fieldName, 'Filtering expression field name is not correct'); + expect(expressions.field).toBe(gridState.advancedFiltering.field, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.advancedFiltering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.advancedFiltering.filteringOperands[i])); diff --git a/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts index 9ecbbdcb66c..5b20391b20e 100644 --- a/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts @@ -516,7 +516,7 @@ class HelperFunctions { } public static verifyFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { - expect(expressions.fieldName).toBe(gridState.filtering.fieldName, 'Filtering expression field name is not correct'); + expect(expressions.field).toBe(gridState.filtering.field, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.filtering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.filtering.filteringOperands[i])); @@ -525,7 +525,7 @@ class HelperFunctions { public static verifyAdvancedFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { if (gridState.advancedFiltering) { - expect(expressions.fieldName).toBe(gridState.advancedFiltering.fieldName, 'Filtering expression field name is not correct'); + expect(expressions.field).toBe(gridState.advancedFiltering.field, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.advancedFiltering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.advancedFiltering.filteringOperands[i])); diff --git a/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts index f87819fc3a2..e554c8edf07 100644 --- a/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts @@ -307,7 +307,7 @@ class HelperFunctions { } public static verifyFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { - expect(expressions.fieldName).toBe(gridState.filtering.fieldName, 'Filtering expression field name is not correct'); + expect(expressions.field).toBe(gridState.filtering.field, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.filtering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.filtering.filteringOperands[i])); @@ -316,7 +316,7 @@ class HelperFunctions { public static verifyAdvancedFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { if (gridState.advancedFiltering) { - expect(expressions.fieldName).toBe(gridState.advancedFiltering.fieldName, 'Filtering expression field name is not correct'); + expect(expressions.field).toBe(gridState.advancedFiltering.field, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.advancedFiltering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.advancedFiltering.filteringOperands[i])); diff --git a/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts b/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts index fa3dad5d6ee..fb688a045d6 100644 --- a/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts +++ b/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts @@ -165,7 +165,7 @@ export class IgxGridSummaryService { private deleteSummaryCache(id, columnName) { if (this.summaryCacheMap.get(id)) { const filteringApplied = columnName && this.grid.filteringExpressionsTree && - this.grid.filteringExpressionsTree.filteringOperands.map((expr) => expr.fieldName).indexOf(columnName) !== -1; + this.grid.filteringExpressionsTree.filteringOperands.map((expr) => expr.field).indexOf(columnName) !== -1; if (columnName && this.summaryCacheMap.get(id).get(columnName) && !filteringApplied) { this.summaryCacheMap.get(id).delete(columnName); } else { diff --git a/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts index 415a83ffb56..201f16476c8 100644 --- a/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts @@ -66,7 +66,7 @@ export class IgxGridToolbarAdvancedFilteringComponent implements AfterViewInit { if (expr instanceof FilteringExpressionsTree) { columnNames.push(...this.extractUniqueFieldNamesFromFilterTree(expr)); } else { - columnNames.push((expr as IFilteringExpression).fieldName); + columnNames.push((expr as IFilteringExpression).field); } }); return [...new Set(columnNames)]; diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts index a4b764972c2..7b9cb92bcea 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts @@ -1398,17 +1398,17 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 299 } ]; @@ -1448,17 +1448,17 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 299 } ]; @@ -1500,7 +1500,7 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 957 }, ]; @@ -1544,17 +1544,17 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 299 } ]; @@ -1615,12 +1615,12 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - fieldName: 'ID', + field: 'ID', searchVal: 998 } ]; diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index afe44018806..5c0d1556461 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -227,7 +227,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @Output() public inEditModeChange = new EventEmitter(); - + @ViewChild('entitySelect', { read: IgxSelectComponent }) protected entitySelect: IgxSelectComponent; @@ -468,7 +468,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { const entity = this.entities.find(el => el.name === this.selectedEntity.name); this.fields = entity.fields; - + if (this.expressionTree) { this.expressionTree.entity = this.selectedEntity.name; this.expressionTree.returnFields = null; @@ -487,9 +487,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - public set selectedReturnFields(value: string | string[]) { + public set selectedReturnFields(value: string[]) { const oldValue = this._selectedReturnFields; - + if (this._selectedReturnFields !== value && oldValue !== value) { this._selectedReturnFields = value; @@ -503,7 +503,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - public get selectedReturnFields(): string | string[] { + public get selectedReturnFields(): string[] { + if (typeof this._selectedReturnFields == 'string') { + return [this._selectedReturnFields]; + } return this._selectedReturnFields; } @@ -562,8 +565,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.cancelOperandAdd(); const operandItem = new ExpressionOperandItem({ - fieldName: null, + field: null, condition: null, + conditionName: null, ignoreCase: true, searchVal: null }, parent); @@ -604,7 +608,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public commitOperandEdit() { if (this._editedExpression) { - this._editedExpression.expression.fieldName = this.selectedField.field; + this._editedExpression.expression.field = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); this._editedExpression.fieldLabel = this.selectedField.label @@ -660,7 +664,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (this._editedExpression) { this._editedExpression.inEditMode = false; - if (!this._editedExpression.expression.fieldName) { + if (!this._editedExpression.expression.field) { this.deleteItem(this._editedExpression); } @@ -753,8 +757,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { expressionItem.hovered = false; this.fields = this.selectedEntity ? this.selectedEntity.fields : null; this.selectedField = - expressionItem.expression.fieldName ? - this.fields.find(field => field.field === expressionItem.expression.fieldName) + expressionItem.expression.field ? + this.fields.find(field => field.field === expressionItem.expression.field) : null; this.selectedCondition = expressionItem.expression.condition ? @@ -1159,8 +1163,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } else { const filteringExpr = expr as IFilteringExpression; const exprCopy: IFilteringExpression = { - fieldName: filteringExpr.fieldName, + field: filteringExpr.field, condition: filteringExpr.condition, + conditionName: filteringExpr.condition.name, searchVal: filteringExpr.searchVal, searchTree: filteringExpr.searchTree, // this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), ignoreCase: filteringExpr.ignoreCase @@ -1170,12 +1175,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (entity) { this.fields = entity.fields; } - const field = this.fields.find(el => el.field === filteringExpr.fieldName); + const field = this.fields.find(el => el.field === filteringExpr.field); operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); this._selectedReturnFields = - !expressionTree.returnFields || expressionTree.returnFields === '*' || expressionTree.returnFields === 'All' + !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') ? this.fields.map(f => f.field) : this.fields.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } @@ -1428,12 +1433,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { name: 'edit', family: 'material', }); - + this.iconService.addIconRef('unfold_less', 'default', { name: 'unfold_less', family: 'material', }); - + this.iconService.addIconRef('unfold_more', 'default', { name: 'unfold_more', family: 'material', diff --git a/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts index 948d811adba..50bd4b50f46 100644 --- a/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts @@ -11,7 +11,7 @@ import { IgxTreeGridComponent } from '../../grids/tree-grid/public_api'; import { ReorderedColumnsComponent, GridIDNameJobTitleComponent, ProductsComponent, - ColumnsAddedOnInitComponent, + ColumnsAddedOnInitComponent, EmptyGridComponent } from '../../test-utils/grid-samples.spec'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; import { first } from 'rxjs/operators'; @@ -341,19 +341,19 @@ describe('CSV Grid Exporter', () => { const grid = fix.componentInstance.grid; const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ID', + field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), }); @@ -379,7 +379,7 @@ describe('CSV Grid Exporter', () => { it('should not export more than one file', async () => { const fix = TestBed.createComponent(EmptyGridComponent); fix.detectChanges(); - + const grid = fix.componentInstance.grid; exporter.export(grid, options); @@ -492,13 +492,13 @@ describe('CSV Grid Exporter', () => { it('Should honor the Advanced filters when exporting', async () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true diff --git a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts index 1693512b9d6..bd936ff899d 100644 --- a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts @@ -589,19 +589,19 @@ describe('Excel Exporter', () => { const grid = fix.componentInstance.grid; const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'ID', + field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), }); @@ -1144,13 +1144,13 @@ describe('Excel Exporter', () => { it('Should honor Advanced filters when exporting', async () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'Age', + field: 'Age', searchVal: 40, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), ignoreCase: true }); tree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), ignoreCase: true diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts index 32d20e821eb..be58b8d2a68 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts @@ -2070,7 +2070,7 @@ export class IgxGridFilteringBindingComponent extends BasicGridComponent impleme this.filterTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - fieldName: 'Downloads', + field: 'Downloads', searchVal: 200 } ]; @@ -2101,7 +2101,7 @@ export class IgxGridAdvancedFilteringBindingComponent extends BasicGridComponent this.filterTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - fieldName: 'Downloads', + field: 'Downloads', searchVal: 200 } ]; diff --git a/src/app/grid-external-filtering/grid-external-filtering.sample.ts b/src/app/grid-external-filtering/grid-external-filtering.sample.ts index 7d31c5e7748..6261bb0815d 100644 --- a/src/app/grid-external-filtering/grid-external-filtering.sample.ts +++ b/src/app/grid-external-filtering/grid-external-filtering.sample.ts @@ -36,28 +36,32 @@ export class GridExternalFilteringComponent implements OnInit, AfterViewInit { public ngAfterViewInit(): void { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'ID', + field: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'a', ignoreCase: true }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ID', + field: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'b', ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'CompanyName', + field: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'c', ignoreCase: true }); tree.filteringOperands.push(orTree); tree.filteringOperands.push({ - fieldName: 'CompanyName', + field: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'd', ignoreCase: true }); diff --git a/src/app/grid-filtering/grid-filtering.sample.ts b/src/app/grid-filtering/grid-filtering.sample.ts index 3a530211a38..85be6257a97 100644 --- a/src/app/grid-filtering/grid-filtering.sample.ts +++ b/src/app/grid-filtering/grid-filtering.sample.ts @@ -19,7 +19,7 @@ export class GridFilteringComponent implements OnInit { protected get sizeStyle() { return `var(--ig-size-${this.size})`; } - + @ViewChild('grid1', { static: true }) public grid1: IgxGridComponent; @@ -75,28 +75,32 @@ export class GridFilteringComponent implements OnInit { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'ID', + field: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'a', ignoreCase: true }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - fieldName: 'ID', + field: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'b', ignoreCase: true }); orTree.filteringOperands.push({ - fieldName: 'CompanyName', + field: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'c', ignoreCase: true }); tree.filteringOperands.push(orTree); tree.filteringOperands.push({ - fieldName: 'CompanyName', + field: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', searchVal: 'd', ignoreCase: true }); diff --git a/src/app/pivot-grid/pivot-grid.sample.ts b/src/app/pivot-grid/pivot-grid.sample.ts index 4ed71a88719..21f4f0df131 100644 --- a/src/app/pivot-grid/pivot-grid.sample.ts +++ b/src/app/pivot-grid/pivot-grid.sample.ts @@ -72,7 +72,8 @@ export class PivotGridSampleComponent { this.filterExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('equals'), - fieldName: 'SellerName', + conditionName: IgxStringFilteringOperand.instance().condition('equals').name, + field: 'SellerName', searchVal: 'Stanley' } ]; diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 919fa8ccc74..477c8dea10b 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -29,7 +29,7 @@ export class QueryBuilderComponent implements OnInit { public ngOnInit(): void { this.entities = [ - { + { name: 'Assays', fields: [ { field: 'Id', dataType: 'number' }, { field: 'CompoundId', dataType: 'number' }, @@ -46,36 +46,41 @@ export class QueryBuilderComponent implements OnInit { } ]; - const innerTree = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', 'Id'); + const innerTree = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', ['Id']); innerTree.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', condition: IgxStringFilteringOperand.instance().condition('equals'), + conditionName: IgxStringFilteringOperand.instance().condition('equals').name, searchVal: 'Hepacity', // ignoreCase: true }); innerTree.filteringOperands.push({ - fieldName: 'EndpointName', + field: 'EndpointName', condition: IgxStringFilteringOperand.instance().condition('equals'), + conditionName: IgxStringFilteringOperand.instance().condition('equals').name, searchVal: 'IC60', // ignoreCase: true }); - const innerTree2 = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', 'Name'); + const innerTree2 = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', ['Name']); innerTree2.filteringOperands.push({ - fieldName: 'Name', + field: 'Name', condition: IgxStringFilteringOperand.instance().condition('null'), + conditionName: IgxStringFilteringOperand.instance().condition('null').name, // ignoreCase: true }); - const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', '*'); + const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', ['*']); tree.filteringOperands.push({ - fieldName: 'Id', + field: 'Id', condition: IgxStringFilteringOperand.instance().condition('in'), + conditionName: IgxStringFilteringOperand.instance().condition('in').name, searchTree: innerTree }); tree.filteringOperands.push({ - fieldName: 'Id', + field: 'Id', condition: IgxStringFilteringOperand.instance().condition('equals'), + conditionName: IgxStringFilteringOperand.instance().condition('equals').name, searchVal: '123', ignoreCase: true }); From bc5ff52271754bc9c0e488dd25b2953852cf69da Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 22 Aug 2024 09:51:46 +0300 Subject: [PATCH 019/147] feat(query-builder): Added missing properties in tests --- .../src/lib/data-operations/data-util.spec.ts | 6 + .../filtering-strategy.spec.ts | 5 + .../grid/grid-filtering-advanced.spec.ts | 120 +++++++++--- .../lib/grids/grid/grid-filtering-ui.spec.ts | 179 ++++++++++-------- .../src/lib/grids/grid/grid-filtering.spec.ts | 77 ++++---- .../lib/grids/grid/grid-row-selection.spec.ts | 2 + .../src/lib/grids/grid/grid.component.spec.ts | 1 + .../hierarchical-grid.virtualization.spec.ts | 5 +- .../lib/grids/pivot-grid/pivot-grid.spec.ts | 3 + .../src/lib/grids/state.directive.spec.ts | 3 +- .../lib/grids/state.hierarchicalgrid.spec.ts | 3 +- .../src/lib/grids/state.treegrid.spec.ts | 3 +- .../tree-grid/tree-grid-selection.spec.ts | 12 ++ .../services/csv/csv-exporter-grid.spec.ts | 5 + .../excel/excel-exporter-grid.spec.ts | 5 + .../src/lib/test-utils/grid-samples.spec.ts | 2 + 16 files changed, 288 insertions(+), 143 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts index a2104839e2d..1d40e4e0afe 100644 --- a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts @@ -352,6 +352,7 @@ const testFilter = () => { { field: 'number', condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', searchVal: 3 } ]; @@ -367,6 +368,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', field: 'string', searchVal: 'row' } @@ -378,6 +380,7 @@ const testFilter = () => { stateIgnoreCase.expressionsTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', field: 'string', ignoreCase: false, searchVal: 'ROW' @@ -401,6 +404,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxDateFilteringOperand.instance().condition('after'), + conditionName: 'after', field: 'date', searchVal: new Date() } @@ -416,6 +420,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxBooleanFilteringOperand.instance().condition('false'), + conditionName: 'false', field: 'boolean' } ]; @@ -431,6 +436,7 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxBooleanFilteringOperand.instance().condition('false'), + conditionName: 'false', field: 'boolean' } ]; diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts index 7ebc7ca7791..a0c79883148 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts @@ -19,6 +19,7 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', field: 'number', searchVal: 1 } @@ -33,12 +34,14 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', field: 'string', ignoreCase: false, searchVal: 'ROW' }, { condition: IgxNumberFilteringOperand.instance().condition('lessThan'), + conditionName: 'lessThan', field: 'number', searchVal: 1 } @@ -50,6 +53,7 @@ describe('Unit testing FilteringStrategy', () => { const rec = data[0]; const res = fs.findMatchByExpression(rec, { condition: IgxBooleanFilteringOperand.instance().condition('false'), + conditionName: 'false', field: 'boolean' }); expect(res).toBeTruthy(); @@ -61,6 +65,7 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', field: 'string', searchVal: 'ROW' } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index 7a1a857a628..7e181904781 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -797,15 +797,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -842,15 +845,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1200,7 +1206,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); @@ -1236,7 +1242,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1274,7 +1280,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1313,7 +1319,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1347,7 +1353,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 20; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1382,15 +1388,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1420,15 +1429,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1483,15 +1495,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1564,10 +1579,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1611,10 +1628,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1663,10 +1682,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1736,15 +1757,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1787,10 +1811,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -1821,10 +1847,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -1857,24 +1885,29 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); const andTree = new FilteringExpressionsTree(FilteringLogic.Or); andTree.filteringOperands.push({ field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); andTree.filteringOperands.push({ field: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push(andTree); @@ -2074,15 +2107,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2120,15 +2156,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2184,15 +2223,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2249,10 +2291,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2305,15 +2349,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2362,15 +2409,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2406,15 +2456,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2462,15 +2515,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2528,15 +2584,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2606,7 +2665,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); @@ -2631,15 +2691,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2708,10 +2771,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2745,10 +2810,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2779,10 +2846,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2813,10 +2882,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2849,15 +2920,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 3fab8cdeb6b..b502bd729a4 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -469,14 +469,16 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { it('Removing second condition removes the And/Or button', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'g', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression1 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'I', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -518,9 +520,10 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { it('Should correctly create FilteringExpressionsTree and populate filterUI.', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); @@ -590,14 +593,16 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { it('Should not select all filter chips when switching columns', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -754,9 +759,10 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); for (let i = 0; i < 10; i++) { const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'I', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); } @@ -820,15 +826,17 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression1 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression2 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'test', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression1); @@ -954,14 +962,16 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -990,14 +1000,16 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'for', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -1039,8 +1051,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1358,9 +1370,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1384,9 +1396,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1442,9 +1454,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1641,9 +1653,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1675,9 +1687,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1713,9 +1725,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1750,8 +1762,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1841,10 +1853,10 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2333,11 +2345,11 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'x', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'y', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'x', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'y', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2409,8 +2421,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2497,8 +2509,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 25, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, - { field: 'Downloads', searchVal: 200, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { field: 'Downloads', searchVal: 25, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThan' }, + { field: 'Downloads', searchVal: 200, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), conditionName: 'lessThan' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2619,9 +2631,10 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); grid.filteringExpressionsTree = filteringExpressionsTree; @@ -3331,8 +3344,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3351,7 +3364,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3385,7 +3398,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3419,8 +3432,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3448,8 +3461,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3474,8 +3487,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3507,8 +3520,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3563,8 +3576,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), conditionName: 'lessThan' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3607,8 +3620,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3626,8 +3639,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3654,8 +3667,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3753,8 +3766,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts index 950416f1382..98ca3427dbb 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts @@ -770,8 +770,8 @@ describe('IgxGrid - Filtering actions #grid', () => { const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); gridExpressionsTree.filteringOperands = [ - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, - { field: 'ID', searchVal: 4, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') } + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo'), conditionName: 'greaterThanOrEqualTo' }, + { field: 'ID', searchVal: 4, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThanOrEqualTo' } ]; grid.filteringExpressionsTree = gridExpressionsTree; @@ -839,14 +839,14 @@ describe('IgxGrid - Filtering actions #grid', () => { it('Should correctly apply two conditions to two columns at once.', fakeAsync(() => { const colDownloadsExprTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); colDownloadsExprTree.filteringOperands = [ - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, - { field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('lessThanOrEqualTo') } + { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo'), conditionName: 'greaterThanOrEqualTo' }, + { field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('lessThanOrEqualTo'), conditionName: 'lessThanOrEqualTo' } ]; const colIdExprTree = new FilteringExpressionsTree(FilteringLogic.And, 'ID'); colIdExprTree.filteringOperands = [ - { field: 'ID', searchVal: 1, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, - { field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { field: 'ID', searchVal: 1, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThan' }, + { field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), conditionName: 'lessThan' } ]; const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); @@ -868,14 +868,16 @@ describe('IgxGrid - Filtering actions #grid', () => { it('Should correctly apply two conditions to number column.', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); const expression = { - fieldName: 'Downloads', + field: 'Downloads', searchVal: 50, - condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }; const expression1 = { - fieldName: 'Downloads', + field: 'Downloads', searchVal: 500, - condition: IgxNumberFilteringOperand.instance().condition('lessThan') + condition: IgxNumberFilteringOperand.instance().condition('lessThan'), + conditionName: 'lessThan' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -890,14 +892,16 @@ describe('IgxGrid - Filtering actions #grid', () => { it('Should correctly apply two conditions to string column.', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -914,14 +918,16 @@ describe('IgxGrid - Filtering actions #grid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ReleaseDate'); const expression = { - fieldName: 'ReleaseDate', + field: 'ReleaseDate', searchVal: null, - condition: IgxDateFilteringOperand.instance().condition('yesterday') + condition: IgxDateFilteringOperand.instance().condition('yesterday'), + conditionName: 'yesterday' }; const expression1 = { - fieldName: 'ReleaseDate', + field: 'ReleaseDate', searchVal: today, - condition: IgxDateFilteringOperand.instance().condition('after') + condition: IgxDateFilteringOperand.instance().condition('after'), + conditionName: 'after' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -937,9 +943,10 @@ describe('IgxGrid - Filtering actions #grid', () => { const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.Or); const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ReleaseDate'); const expression = { - fieldName: 'ReleaseDate', + field: 'ReleaseDate', searchVal: null, - condition: IgxDateFilteringOperand.instance().condition('yesterday') + condition: IgxDateFilteringOperand.instance().condition('yesterday'), + conditionName: 'yesterday', }; filteringExpressionsTree.filteringOperands.push(expression); gridExpressionsTree.filteringOperands.push(filteringExpressionsTree); @@ -972,31 +979,36 @@ describe('IgxGrid - Filtering actions #grid', () => { it('Should generate the expressions UI list correctly.', fakeAsync(() => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression11 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression12 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'jQuery', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression2 = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression21 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression22 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'jQuery', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; expression1.filteringOperands.push(expression11); expression1.filteringOperands.push(expression12); @@ -1061,9 +1073,10 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression1 = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); const expression11 = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; // Verify results after filtering. diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts index 44be1015ce8..61a7939498f 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts @@ -2016,11 +2016,13 @@ describe('IgxGrid - Row Selection #grid', () => { field: 'UnitsInStock', searchVal: 0, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); tree.filteringOperands.push({ field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 21109a6b21e..375601e8181 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -563,6 +563,7 @@ describe('IgxGrid Component Tests #grid', () => { grid.filteringExpressionsTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('equals'), + conditionName: 'equals', field: 'index', searchVal: 0 } diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts index f97dff86967..ce3952abfb8 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts @@ -107,9 +107,10 @@ describe('IgxHierarchicalGrid Virtualization #hGrid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - fieldName: 'ProductName', + field: 'ProductName', searchVal: 'Product: A0', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); childGrid.filter('ProductName', null, filteringExpressionsTree); diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts index 273b5d1e2e5..2c329dc67c7 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts @@ -227,6 +227,7 @@ describe('IgxPivotGrid #pivotGrid', () => { filteringExpressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('equals'), + conditionName: 'equals', field: 'SellerName', searchVal: 'Stanley' } @@ -2731,6 +2732,7 @@ describe('IgxPivotGrid #pivotGrid', () => { filterColumnExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('in'), + conditionName: 'in', field: 'City', searchVal: new Set(['Ciudad de la Costa']) } @@ -2739,6 +2741,7 @@ describe('IgxPivotGrid #pivotGrid', () => { filterRowExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('in'), + conditionName: 'in', field: 'ProductCategory', searchVal: new Set(['Bikes']) } diff --git a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts index 292fb7741c0..277e2cdb0db 100644 --- a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts @@ -103,7 +103,8 @@ describe('IgxGridState - input properties #grid', () => { const productFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const productExpression = { condition: IgxBooleanFilteringOperand.instance().condition('true'), - fieldName: 'InStock', + conditionName: 'true', + field: 'InStock', ignoreCase: true }; productFilteringExpressionsTree.filteringOperands.push(productExpression); diff --git a/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts index 5b20391b20e..a33619250b9 100644 --- a/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts @@ -109,7 +109,8 @@ describe('IgxHierarchicalGridState - input properties #hGrid', () => { const productFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const productExpression = { condition: IgxStringFilteringOperand.instance().condition('contains'), - fieldName: 'ProductName', + conditionName: 'contains', + field: 'ProductName', ignoreCase: true, searchVal: 'A0' }; diff --git a/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts index e554c8edf07..36e44bd8345 100644 --- a/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts @@ -101,7 +101,8 @@ describe('IgxTreeGridState - input properties #tGrid', () => { const productFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'Age'); const productExpression = { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - fieldName: 'Age', + conditionName: 'greaterThan', + field: 'Age', ignoreCase: true, searchVal: 35 }; diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts index 7b9cb92bcea..0078e48347c 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts @@ -1398,16 +1398,19 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 299 } @@ -1448,16 +1451,19 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 299 } @@ -1500,6 +1506,7 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 957 }, @@ -1544,16 +1551,19 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 299 } @@ -1615,11 +1625,13 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), + conditionName: 'doesNotEqual', field: 'ID', searchVal: 998 } diff --git a/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts index 50bd4b50f46..326dc517e84 100644 --- a/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts @@ -344,18 +344,21 @@ describe('CSV Grid Exporter', () => { field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; @@ -495,12 +498,14 @@ describe('CSV Grid Exporter', () => { field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); treeGrid.advancedFilteringExpressionsTree = tree; diff --git a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts index bd936ff899d..e08f1c828f8 100644 --- a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts @@ -592,18 +592,21 @@ describe('Excel Exporter', () => { field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'contains', }); grid.advancedFilteringExpressionsTree = tree; @@ -1147,12 +1150,14 @@ describe('Excel Exporter', () => { field: 'Age', searchVal: 40, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), + conditionName: 'lessThan', ignoreCase: true }); tree.filteringOperands.push({ field: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts index be58b8d2a68..cfce068a807 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts @@ -2070,6 +2070,7 @@ export class IgxGridFilteringBindingComponent extends BasicGridComponent impleme this.filterTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', field: 'Downloads', searchVal: 200 } @@ -2101,6 +2102,7 @@ export class IgxGridAdvancedFilteringBindingComponent extends BasicGridComponent this.filterTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', field: 'Downloads', searchVal: 200 } From 15fb61ac50cd837c7f7b1738ef1d6b1815e373bc Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 22 Aug 2024 10:39:55 +0300 Subject: [PATCH 020/147] feat(query-builder): add confirmation dialog on entity change --- .../query-builder-tree.component.html | 17 ++++- .../query-builder-tree.component.ts | 73 ++++++++++++------- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index b2fc38376e3..99f85661f3e 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -411,7 +411,8 @@
@@ -433,8 +434,8 @@
> class="igx-query-builder__outlet" (pointerdown)="onOutletPointerDown($event)" >
+ + + \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index e1ea8ebebc5..85cb3cd8b7a 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -41,6 +41,8 @@ import { IgxComboComponent } from "../combo/combo.component"; import { IgxLabelDirective } from '../input-group/public_api'; import { IgxComboHeaderDirective } from '../combo/public_api'; import { IgxCheckboxComponent } from "../checkbox/checkbox.component"; +import { IgxDialogComponent } from "../dialog/dialog.component"; +import { ISelectionEventArgs } from '../drop-down/drop-down.common'; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; @@ -110,7 +112,7 @@ class ExpressionOperandItem extends ExpressionItem { selector: 'igx-query-builder-tree', templateUrl: './query-builder-tree.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective, IgxComboHeaderDirective, IgxCheckboxComponent] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxIconComponent, IgxChipComponent, IgxPrefixDirective, IgxSuffixDirective, IgxSelectComponent, FormsModule, NgFor, IgxSelectItemComponent, IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, NgTemplateOutlet, NgClass, IgxToggleDirective, IgxButtonGroupComponent, IgxOverlayOutletDirective, DatePipe, IgxFieldFormatterPipe, IgxIconButtonDirective, IgxToggleActionDirective, IgxComboComponent, IgxLabelDirective, IgxComboHeaderDirective, IgxCheckboxComponent, IgxDialogComponent] }) export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** @@ -253,6 +255,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @ViewChild('addConditionButton', { read: ElementRef }) private addConditionButton: ElementRef; + + @ViewChild('entityChangeDialog', { read: IgxDialogComponent }) + private entityChangeDialog: IgxDialogComponent; /** * @hidden @internal @@ -412,6 +417,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { private _fields: FieldType[]; private _expressionTree: IExpressionTree; private _locale; + private _entityNewValue: EntityType; private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); private _positionSettings = { @@ -455,39 +461,50 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - public set selectedEntity(value: EntityType) { - let oldValue = null; - if (this.expressionTree) { - oldValue = this.expressionTree.entity; - } - if (this._selectedEntity !== value) { - this._selectedEntity = value; - this.selectedReturnFields = null; - this.fields = value ? value.fields : null; - if (oldValue && this._selectedEntity /*&& this._selectedEntity !== oldValue*/) { - this.selectedField = null; - this.selectedCondition = null; - this.searchValue = null; - this.cdr.detectChanges(); - } - } - - const entity = this.entities.find(el => el.name === this.selectedEntity.name); - this.fields = entity.fields; + public get selectedEntity(): EntityType { + return this._selectedEntity; + } - if (this.expressionTree) { - this.expressionTree.entity = this.selectedEntity.name; - this.expressionTree.returnFields = null; + /** + * @hidden @internal + */ + public onEntitySelectChanging(event: ISelectionEventArgs) { + event.cancel = true; + this._entityNewValue = event.newSelection.value; + this.entityChangeDialog.open(); + } - // TODO: show dialog alert - } + /** + * @hidden + */ + public onEntityChangeCancel() { + this.entityChangeDialog.close(); + this.entitySelect.close(); + this._entityNewValue = null; } /** - * @hidden @internal + * @hidden */ - public get selectedEntity(): EntityType { - return this._selectedEntity; + public onEntityChangeConfirm() { + this._selectedEntity = this._entityNewValue; + this.fields = this._entityNewValue ? this._entityNewValue.fields : []; + this._selectedReturnFields = this._entityNewValue.fields.map(f => f.field); + if (this.expressionTree) { + this.expressionTree.entity = this._selectedEntity.name; + this.expressionTree.returnFields = []; + this.expressionTree.filteringOperands = []; + + this.expressionTreeChange.emit(this._expressionTree); + + this._editedExpression = null; + this.addAndGroup(); + } + + this.entityChangeDialog.close(); + this.entitySelect.close(); + + this._entityNewValue = null; } /** From 56b400afd956146677cd67da09a0765f89f5343b Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 23 Aug 2024 13:51:27 +0300 Subject: [PATCH 021/147] feat(query-builder): add tooltip and bug fixes --- .../query-builder-tree.component.html | 11 ++++ .../query-builder-tree.component.ts | 64 +++++++++++++++++-- src/app/query-builder/query-builder.sample.ts | 6 +- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 99f85661f3e..e65598e7fcc 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -76,6 +76,8 @@
(mouseleave)="expressionItem.hovered = false" > +
+ {{ + expressionItem.expression.searchTree ? + expressionItem.expression.searchTree.returnFields : + expressionItem.expression.searchVal ? + expressionItem.expression.searchVal : + '' + }} +
el.name === expressionTree.entity); + if (expressionTree.entity) { + entityName = expressionTree.entity; + } + const entity = this.entities.find(el => el.name === entityName); if (entity) { this.fields = entity.fields; } @@ -1225,7 +1275,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { operandItem.expanded = true; } groupItem.children.push(operandItem); - this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); + this._selectedEntity = this.entities.find(el => el.name === entityName); this._selectedReturnFields = !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') ? this.fields.map(f => f.field) diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 477c8dea10b..8d74501fd51 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -35,13 +35,15 @@ export class QueryBuilderComponent implements OnInit { { field: 'CompoundId', dataType: 'number' }, { field: 'Name', dataType: 'string' }, { field: 'EndpointName', dataType: 'string' }, - { field: 'EndpointValue', dataType: 'string' } + { field: 'EndpointValue', dataType: 'string' }, + { field: 'Date', dataType: 'date' } ] }, { name: 'Compounds', fields: [ { field: 'Id', dataType: 'number' }, - { field: 'Structure', dataType: 'string' } + { field: 'Structure', dataType: 'string' }, + { field: 'Date', dataType: 'date' } ] } ]; From 1a1df9aa9b81a85f92681cbf549ca568005cbb1a Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 23 Aug 2024 15:05:40 +0300 Subject: [PATCH 022/147] feat(query-builder): move icon service back to query builder --- .../query-builder-tree.component.html | 1 + .../query-builder-tree.component.ts | 76 ++----------------- .../query-builder/query-builder.component.ts | 74 ++++++++++++++++-- 3 files changed, 76 insertions(+), 75 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index e65598e7fcc..c879094a182 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -439,6 +439,7 @@
[data]="fields" [displayKey]="'field'" [valueKey]="'field'" + [disabled]="!selectedEntity" [(ngModel)]="selectedReturnFields" searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 87e9813060f..1eabea43177 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -6,7 +6,6 @@ import { } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { Subject } from 'rxjs'; -import { editor } from '@igniteui/material-icons-extended'; import { IButtonGroupEventArgs, IgxButtonGroupComponent } from '../buttonGroup/buttonGroup.component'; import { IgxChipComponent } from '../chips/chip.component'; import { IQueryBuilderResourceStrings, QueryBuilderResourceStringsEN } from '../core/i18n/query-builder-resources'; @@ -22,7 +21,6 @@ import { IgxDateTimeEditorDirective } from '../directives/date-time-editor/date- import { IgxOverlayOutletDirective, IgxToggleActionDirective, IgxToggleDirective } from '../directives/toggle/toggle.directive'; import { FieldType, EntityType } from '../grids/common/grid.interface'; -import { IgxIconService } from '../icon/icon.service'; import { IgxSelectComponent } from '../select/select.component'; import { HorizontalAlignment, OverlaySettings, Point, VerticalAlignment } from '../services/overlay/utilities'; import { AbsoluteScrollStrategy, AutoPositionStrategy, CloseScrollStrategy, ConnectedPositioningStrategy } from '../services/public_api'; @@ -191,8 +189,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._fields = fields; if (this._fields) { - this.registerSVGIcons(); - this._fields.forEach(field => { this.setFilters(field); this.setFormat(field); @@ -213,6 +209,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @Input() public set expressionTree(expressionTree: IExpressionTree) { this._expressionTree = expressionTree; + if (!expressionTree) { + this._selectedEntity = null; + this._selectedReturnFields = []; + } this.init(); } @@ -470,7 +470,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { }; constructor(public cdr: ChangeDetectorRef, - protected iconService: IgxIconService, protected platform: PlatformUtil, protected el: ElementRef, @Inject(LOCALE_ID) protected _localeId: string) { @@ -701,8 +700,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); - this._expressionTree.entity = this.selectedEntity.name; - this._expressionTree.returnFields = this.selectedReturnFields; + if (this._expressionTree) { + this._expressionTree.entity = this.selectedEntity.name; + this._expressionTree.returnFields = this.selectedReturnFields; + } this.expressionTreeChange.emit(this._expressionTree); } @@ -1480,66 +1481,5 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.rootGroup = this.createExpressionGroupItem(this.expressionTree); this.currentGroup = this.rootGroup; } - - private registerSVGIcons(): void { - const editorIcons = editor as any[]; - - editorIcons.forEach((icon) => { - this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons'); - this.iconService.addIconRef(icon.name, 'default', { - name: icon.name, - family: 'imx-icons' - }); - }); - - const inIcon = ''; - this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons'); - this.iconService.addIconRef('in', 'default', { - name: 'in', - family: 'imx-icons' - }); - - const notInIcon = ''; - this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons'); - this.iconService.addIconRef('not-in', 'default', { - name: 'not-in', - family: 'imx-icons' - }); - - this.iconService.addIconRef('add', 'default', { - name: 'add', - family: 'material', - }); - - this.iconService.addIconRef('close', 'default', { - name: 'close', - family: 'material', - }); - - this.iconService.addIconRef('check', 'default', { - name: 'check', - family: 'material', - }); - - this.iconService.addIconRef('delete', 'default', { - name: 'delete', - family: 'material', - }); - - this.iconService.addIconRef('edit', 'default', { - name: 'edit', - family: 'material', - }); - - this.iconService.addIconRef('unfold_less', 'default', { - name: 'unfold_less', - family: 'material', - }); - - this.iconService.addIconRef('unfold_more', 'default', { - name: 'unfold_more', - family: 'material', - }); - } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index dd3d2d2e0df..526156a0e5f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, ContentChild, EventEmitter, Output } from '@angular/core'; +import { ContentChild, EventEmitter, Output } from '@angular/core'; import { NgIf} from '@angular/common'; import { Component, Input, ViewChild, ElementRef, OnDestroy, HostBinding @@ -11,6 +11,8 @@ import { EntityType } from '../grids/common/grid.interface'; import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component'; import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component'; +import { IgxIconService } from '../icon/icon.service'; +import { editor } from '@igniteui/material-icons-extended'; /** * A component used for operating with complex filters by creating or editing conditions @@ -29,7 +31,7 @@ import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component'; standalone: true, imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxQueryBuilderTreeComponent] }) -export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { +export class IgxQueryBuilderComponent implements OnDestroy { /** * @hidden @internal */ @@ -121,11 +123,8 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { private destroy$ = new Subject(); private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); - /** - * @hidden @internal - */ - public ngAfterViewInit(): void { - // this._overlaySettings.outlet = this.overlayOutlet; + constructor(protected iconService: IgxIconService) { + this.registerSVGIcons(); } /** @@ -183,5 +182,66 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy { this.expressionTree = tree; this.expressionTreeChange.emit(); } + + private registerSVGIcons(): void { + const editorIcons = editor as any[]; + + editorIcons.forEach((icon) => { + this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons'); + this.iconService.addIconRef(icon.name, 'default', { + name: icon.name, + family: 'imx-icons' + }); + }); + + const inIcon = ''; + this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons'); + this.iconService.addIconRef('in', 'default', { + name: 'in', + family: 'imx-icons' + }); + + const notInIcon = ''; + this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons'); + this.iconService.addIconRef('not-in', 'default', { + name: 'not-in', + family: 'imx-icons' + }); + + this.iconService.addIconRef('add', 'default', { + name: 'add', + family: 'material', + }); + + this.iconService.addIconRef('close', 'default', { + name: 'close', + family: 'material', + }); + + this.iconService.addIconRef('check', 'default', { + name: 'check', + family: 'material', + }); + + this.iconService.addIconRef('delete', 'default', { + name: 'delete', + family: 'material', + }); + + this.iconService.addIconRef('edit', 'default', { + name: 'edit', + family: 'material', + }); + + this.iconService.addIconRef('unfold_less', 'default', { + name: 'unfold_less', + family: 'material', + }); + + this.iconService.addIconRef('unfold_more', 'default', { + name: 'unfold_more', + family: 'material', + }); + } } From 5f2a72bad88f3ad3c43bf484261eeaed5e8eeb8a Mon Sep 17 00:00:00 2001 From: teodosia Date: Fri, 23 Aug 2024 15:49:46 +0300 Subject: [PATCH 023/147] feat(query-builder): fix lint errors and add header import in dev demo --- .../query-builder/query-builder-tree.component.html | 6 +++--- .../lib/query-builder/query-builder-tree.component.ts | 10 +++++----- src/app/query-builder/query-builder.sample.ts | 5 +++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index c879094a182..d7db1166296 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -131,7 +131,7 @@
{{ expressionItem.expression.searchTree ? expressionItem.expression.searchTree.returnFields : - expressionItem.expression.searchVal ? + expressionItem.expression.searchVal ? expressionItem.expression.searchVal : '' }} @@ -446,7 +446,7 @@
> rightButtonLabel="Confirm" (leftButtonSelect)="onEntityChangeCancel()" (rightButtonSelect)="onEntityChangeConfirm()"> - \ No newline at end of file + diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 1eabea43177..79cf8f9bbcb 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -127,7 +127,7 @@ class ExpressionOperandItem extends ExpressionItem { IgxInputGroupComponent, IgxInputDirective, IgxDatePickerComponent, - IgxPickerToggleComponent, + IgxPickerToggleComponent, IgxPickerClearComponent, IgxTimePickerComponent, IgxDateTimeEditorDirective, @@ -292,7 +292,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @ViewChild('addConditionButton', { read: ElementRef }) private addConditionButton: ElementRef; - + @ViewChild('entityChangeDialog', { read: IgxDialogComponent }) private entityChangeDialog: IgxDialogComponent; @@ -510,7 +510,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (event.oldSelection.value) { this.entityChangeDialog.open(); } else { - this.onEntityChangeConfirm(); + this.onEntityChangeConfirm(); } } @@ -543,7 +543,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.entityChangeDialog.close(); this.entitySelect.close(); - + this._entityNewValue = null; } @@ -1145,7 +1145,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } - public onSelectAllClicked(event) { + public onSelectAllClicked(_event) { if ( (this._selectedReturnFields.length > 0 && this._selectedReturnFields.length < this._selectedEntity.fields.length) || this._selectedReturnFields.length == this._selectedEntity.fields.length diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 8d74501fd51..be2395f8251 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -6,7 +6,8 @@ import { FilteringExpressionsTree, IgxStringFilteringOperand, IExpressionTree, IgxButtonDirective, IgxButtonGroupComponent, - IgxRippleDirective} from 'igniteui-angular'; + IgxRippleDirective, + IgxQueryBuilderHeaderComponent} from 'igniteui-angular'; import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; @@ -16,7 +17,7 @@ import { SizeSelectorComponent } from '../size-selector/size-selector.component' styleUrls: ['query-builder.sample.scss'], templateUrl: 'query-builder.sample.html', standalone: true, - imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent] + imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent] }) export class QueryBuilderComponent implements OnInit { @ViewChild('queryBuilder', { static: true }) From ee6f1d31b0006d51372acd78c594d61405d34a52 Mon Sep 17 00:00:00 2001 From: teodosia Date: Mon, 26 Aug 2024 16:59:40 +0300 Subject: [PATCH 024/147] feat(query-builder): fix circular dependency issue --- .../src/lib/data-operations/filtering-expression.interface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index 3770b6f8a76..9f3e50156b4 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -1,6 +1,6 @@ -import { IExpressionTree } from 'igniteui-angular'; import { IFilteringOperation } from './filtering-condition'; import { Serializable } from 'node:child_process'; +import { IExpressionTree } from './filtering-expressions-tree'; /* mustCoerceToInt */ export enum FilteringLogic { From 822ca70054b8100f84f711875a5d8e6fe9ca2ea3 Mon Sep 17 00:00:00 2001 From: gedinakova Date: Tue, 27 Aug 2024 18:53:57 +0300 Subject: [PATCH 025/147] chore(*): Fix error with type missmatch for date pickers in ESF custom dialog. --- .../excel-style-date-expression.component.html | 6 +++--- .../excel-style-date-expression.component.ts | 11 ++++++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.html b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.html index 81f66dbb30d..7799906b1eb 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.html @@ -15,7 +15,7 @@ diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.ts index 09051d9f8d9..12bf4bce2ee 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-date-expression.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild } from '@angular/core'; +import { Component, Input, ViewChild } from '@angular/core'; import { IgxExcelStyleDefaultExpressionComponent } from './excel-style-default-expression.component'; import { IgxInputDirective } from '../../../directives/input/input.directive'; import { IgxTimePickerComponent } from '../../../time-picker/time-picker.component'; @@ -33,6 +33,15 @@ export class IgxExcelStyleDateExpressionComponent extends IgxExcelStyleDefaultEx @ViewChild('picker') private picker: IgxDatePickerComponent | IgxTimePickerComponent; + @Input() + public get searchVal(): any { + return this.expressionUI.expression.searchVal; + } + + public set searchVal(value: any) { + this.expressionUI.expression.searchVal = value ? new Date(Date.parse(value.toString())) : null; + } + protected override get inputValuesElement() { return this.picker?.getEditElement() || this.input?.nativeElement; } From 9eb2d093c74132f5352a68e7b701424792e0790a Mon Sep 17 00:00:00 2001 From: desig9stein Date: Wed, 28 Aug 2024 10:29:39 +0300 Subject: [PATCH 026/147] refactor(query-builder): implement the new styling --- .../styles/components/grid/_grid-theme.scss | 2 + .../_query-builder-component.scss | 25 +++ .../query-builder/_query-builder-theme.scss | 159 ++++++++++-------- .../query-builder-tree.component.html | 53 +++--- .../query-builder-tree.component.ts | 37 +++- .../query-builder.component.html | 2 +- .../query-builder/query-builder.component.ts | 6 +- .../query-builder/query-builder.sample.html | 3 +- .../query-builder/query-builder.sample.scss | 17 +- 9 files changed, 188 insertions(+), 116 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss index 6ecc643218e..7e8b8a35253 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/grid/_grid-theme.scss @@ -3043,6 +3043,8 @@ --igx-query-builder-background-or: #{var-get($theme, 'filtering-background-or')}; --igx-query-builder-background-or--focus: #{var-get($theme, 'filtering-background-or--focus')}; box-shadow: none; + border: none; + border-radius: inherit; } igx-query-builder-header { diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-component.scss index ac165d20c90..87bd126e3f3 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-component.scss @@ -8,6 +8,7 @@ @include _advanced-filtering-tree(); @include _advanced-filtering-con-menu(); @include _advanced-filtering-empty(); + @include _query-builder-tree(); @include b(igx-query-builder) { $this: bem--selector-to-string(&); @@ -26,6 +27,14 @@ @extend %advanced-filter__main !optional; } + @include e(root) { + @extend %advanced-filter__root !optional; + } + + @include e(root-actions) { + @extend %advanced-filter__root-actions !optional; + } + @include e(outlet) { @extend %advanced-filter__outlet !optional; } @@ -111,6 +120,10 @@ @include e(inputs-actions) { @extend %filter-tree__inputs-actions !optional; } + + @include e(details-button) { + @extend %filter-tree-details-button !optional;; + } } } @@ -137,3 +150,15 @@ } } } + +@mixin _query-builder-tree { + @include b(igx-query-builder-tree) { + @extend %query-builder-tree !optional; + + @for $i from 0 through 5 { + @include m(level-#{$i}) { + @extend %query-level-#{$i} !optional; + } + } + } +} diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 3cbdf6a3c0a..7d1c7e5adf5 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -28,6 +28,11 @@ $background-and--focus: null, $background-or: null, $background-or--focus: null, + $border-color: null, + $border-radius: null, + $query-background: null, + $query-border-color: null, + $query-border-radius: null, ) { $name: 'igx-query-builder'; $selector: 'igx-query-builder'; @@ -49,7 +54,6 @@ @return extend($theme, ( name: $name, selector: $selector, - background: $background, header-background: $header-background, header-foreground: $header-foreground, @@ -57,6 +61,11 @@ background-and--focus: $background-and--focus, background-or: $background-or, background-or--focus: $background-or--focus, + border-color: $border-color, + query-background: $query-background, + query-border-color: $query-border-color, + query-border-radius: $query-border-radius, + border-radius: $border-radius, _meta: map.merge(if($meta, $meta, ()), ( variant: map.get($schema, '_meta', 'theme') )), @@ -92,16 +101,61 @@ compact: rem(8px) ); + $backgrounds: (var-get($theme, 'background'), var-get($theme, 'query-background')); + + $gap: sizable( + map.get($vertical-space, 'compact'), + map.get($vertical-space, 'cosy'), + map.get($vertical-space, 'comfortable') + ); + + $border-radius: var-get($theme, 'border-radius'); +; + $max-depth: 10; $icon-size: rem(18px); %advanced-filter { + --component-size: var(--ig-size, var(--ig-size-large)); + @include sizable(); width: auto; min-width: rem(660px); background-color: var-get($theme, 'background'); - border-radius: border-radius(rem(4px)); - box-shadow: elevation(12); + border-radius: $border-radius; + border: rem(1px) solid var-get($theme, 'border-color'); + } + + %query-builder-tree { + width: 100%; + + border-radius: var-get($theme, 'query-border-radius'); + + %query-builder-tree { + border: rem(1px) solid var-get($theme, 'query-border-color'); + + %filter-empty { + display: none; + } + + %advanced-filter__main { + padding: pad-inline( + map.get($vertical-space, 'compact'), + map.get($vertical-space, 'cosy'), + map.get($vertical-space, 'comfortable') + ); + } + } + } + + @for $i from 0 through 5 { + %query-level-#{$i} { + background: nth($backgrounds, ($i % length($backgrounds)) + 1); + } + } + + %filter-tree-details-button { + margin-inline-start: auto; } %advanced-filter__header { @@ -110,6 +164,7 @@ background-color: var-get($theme, 'header-background'); color: var-get($theme, 'header-foreground'); user-select: none; + border-radius: $border-radius $border-radius 0 0; h4, .ig-typography__h6 { @@ -118,16 +173,26 @@ } %advanced-filter__main { - display: block; + display: flex; overflow: auto; - // min-height: pad(rem(138px), rem(164px), rem(214px)); - // max-height: pad(rem(305px), rem(394px), rem(468px)); + gap: $gap; + } - [igxButton] + [igxButton] { - margin-inline-start: rem(8px); + %advanced-filter__root { + display: flex; + flex-direction: column; + flex-grow: 1; + + > * { + flex-grow: 1; } } + %advanced-filter__root-actions { + display: flex; + gap: rem(8px); + } + %advanced-filter__header, %advanced-filter__main { padding-inline: pad-inline( @@ -149,16 +214,7 @@ %filter-tree { display: flex; - - + %filter-tree, - + %filter-tree__expression-item, - + %filter-tree__inputs { - margin-top: pad( - map.get($vertical-space, 'compact'), - map.get($vertical-space, 'cosy'), - map.get($vertical-space, 'comfortable') - ); - } + width: 100%; } %filter-tree__line { @@ -167,7 +223,7 @@ background-color: white; margin-inline-end: pad(rem(8px), rem(12px), rem(16px)); outline-style: none; - border-radius: border-radius(rem(4px)); + border-radius: $border-radius; cursor: pointer; } @@ -199,21 +255,16 @@ display: flex; flex-direction: column; align-items: flex-start; + flex-grow: 1; + gap: $gap; } %filter-tree__expression-item { display: flex; align-items: center; - - + %filter-tree, - + %filter-tree__inputs, - + %filter-tree__expression-item { - margin-top: pad( - map.get($vertical-space, 'compact'), - map.get($vertical-space, 'cosy'), - map.get($vertical-space, 'comfortable') - ); - } + width: 100%; + gap: $gap; + position: relative; > igx-chip { @container style(--ig-size: 1) { @@ -246,7 +297,7 @@ %filter-tree__expression-actions { display: inline-flex; - margin: 0 rem(8px); + gap: $gap; igx-icon { cursor: pointer; @@ -258,10 +309,6 @@ color: color(null, 'gray', 800); } } - - igx-icon + igx-icon { - margin-inline-start: rem(8px); - } } %filter-tree__expression-condition { @@ -272,62 +319,32 @@ display: flex; justify-content: space-between; align-items: center; - margin-top: pad( - map.get($vertical-space, 'compact'), - map.get($vertical-space, 'cosy'), - map.get($vertical-space, 'comfortable') - ); - - + %filter-tree, - + %filter-tree__expression-item, - + %filter-tree__inputs { - margin-top: pad( - map.get($vertical-space, 'compact'), - map.get($vertical-space, 'cosy'), - map.get($vertical-space, 'comfortable') - ); - } + gap: rem(8px); } %filter-tree__inputs { display: flex; justify-content: space-between; align-items: center; + gap: rem(8px); igx-input-group { --ig-size: 1; } - igx-select + igx-select, - igx-select + igx-input-group, - igx-select + igx-date-picker, - igx-select + igx-time-picker, - igx-input-group + igx-select { - margin-inline-start: rem(8px); - } - - + %filter-tree, - + %filter-tree__expression-item { - margin-top: pad( - map.get($vertical-space, 'compact'), - map.get($vertical-space, 'cosy'), - map.get($vertical-space, 'comfortable') - ); + &:empty { + display: none !important; } } %filter-tree__inputs-actions { display: flex; - margin: 0 rem(8px); + gap: $gap; align-items: center; [igxIconButton] { transition: none; } - - [igxIconButton] + [igxIconButton] { - margin-inline-start: rem(8px); - } } %filter-legend { @@ -347,7 +364,7 @@ width: rem(24px); height: rem(24px); background: white; - border-radius: border-radius(rem(4px)); + border-radius: $border-radius; margin-inline-end: rem(8px); } @@ -372,7 +389,7 @@ background-color: var-get($theme, 'background'); padding: rem(16px); margin-inline-start: rem(16px); - border-radius: border-radius(rem(4px)); + border-radius: $border-radius; border: rem(1px) solid color(null, 'gray', 200); > [igxButton] + [igxButton], diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index d7db1166296..fc16d451f94 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -12,27 +12,30 @@ (scroll)="onExpressionsScrolled()" > - - - +
+
+ -
-
- {{ this.resourceStrings.igx_query_builder_initial_text }} -
+ +
+
+
+ {{ this.resourceStrings.igx_query_builder_initial_text }} +
+
@@ -169,9 +172,8 @@
>
-
+
{{this.resourceStrings.igx_query_builder_details}} @@ -183,7 +185,6 @@
*ngIf="expressionItem.inEditMode" #editingInputsContainer class="igx-filter-tree__inputs" - style = "margin-top: 10px" > >
-
+
- \ No newline at end of file + diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 526156a0e5f..40c591dfd27 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -71,7 +71,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { // } // } - + @Input() public expressionTree: IExpressionTree; @@ -113,13 +113,13 @@ export class IgxQueryBuilderComponent implements OnDestroy { */ @ContentChild(IgxQueryBuilderHeaderComponent) public headerContent: IgxQueryBuilderHeaderComponent; - + /** * @hidden @internal */ @ViewChild(IgxQueryBuilderTreeComponent) public queryTree: IgxQueryBuilderTreeComponent; - + private destroy$ = new Subject(); private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index c6a6d9001b5..9e596150045 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -1,8 +1,9 @@
- + diff --git a/src/app/query-builder/query-builder.sample.scss b/src/app/query-builder/query-builder.sample.scss index 95306f0956a..ef0203d52eb 100644 --- a/src/app/query-builder/query-builder.sample.scss +++ b/src/app/query-builder/query-builder.sample.scss @@ -3,6 +3,12 @@ max-width: 900px; } +.sample-column { + display: flex; + flex-direction: column; + gap: 16px; +} + .sample-buttons { margin-top: 24px; display: flex; @@ -12,8 +18,11 @@ .output-area{ overflow-y: auto; height: 400px; - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12); - margin-top: 15px; + border: 1px solid hsla(var(--ig-gray-300)); border-radius: 4px; - padding-left: 16px; -} \ No newline at end of file + padding: 16px; + + pre { + margin-block: 0; + } +} From 81af7ab301d43592b486ff355de04cb9bb0c69c6 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Mon, 26 Aug 2024 11:58:43 +0300 Subject: [PATCH 027/147] feat(query-builder): Don't show group controls while adding a new group --- .../query-builder-tree.component.html | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index fc16d451f94..40cf47f9659 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -473,7 +473,7 @@
@@ -484,19 +484,6 @@
" > -
From 3ad70eb51febb8a4694604ac874658fa2695637a Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Tue, 27 Aug 2024 13:07:49 +0300 Subject: [PATCH 028/147] feat(query-builder): Partial fix for filtering logic changes in children --- .../lib/query-builder/query-builder-tree.component.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 2deaccf956b..7f0f3e2a058 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -203,7 +203,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * Returns the expression tree. */ - public get expressionTree(): IExpressionTree { + public get expressionTree(): IExpressionTree { return this._expressionTree; } @@ -874,8 +874,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (!this.selectedField) { this.fieldSelect.input.nativeElement.focus(); - } else if (this.selectedField.filters.condition(this.selectedCondition).isUnary) { - this.conditionSelect.input.nativeElement.focus(); + } else if (this.selectedField.filters.condition(this.selectedCondition).isUnary) { + this.conditionSelect.input.nativeElement.focus(); } else { const input = this.searchValueInput?.nativeElement || this.picker?.getEditElement(); input.focus(); @@ -968,6 +968,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public onGroupClick(groupItem: ExpressionGroupItem) { + const firstChild = groupItem.children[0] as ExpressionOperandItem; + if (!this.isInEditMode() && firstChild) { + this.enterExpressionEdit(firstChild); + } this.toggleGroup(groupItem); } From ec70f6a56362986c516a3f9c38f01831471933ac Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Tue, 27 Aug 2024 15:01:56 +0300 Subject: [PATCH 029/147] feat(query-builder): Made select dropdowns close on outside click --- .../src/lib/query-builder/query-builder-tree.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 7f0f3e2a058..bd35cebfc55 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -427,7 +427,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public fieldSelectOverlaySettings: OverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, - closeOnOutsideClick: false + closeOnOutsideClick: true }; /** @@ -436,7 +436,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public conditionSelectOverlaySettings: OverlaySettings = { scrollStrategy: new AbsoluteScrollStrategy(), modal: false, - closeOnOutsideClick: false + closeOnOutsideClick: true }; private destroy$ = new Subject(); From b7a52093f8d63d1dbd92af2a094344849f3496c1 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Tue, 27 Aug 2024 15:46:01 +0300 Subject: [PATCH 030/147] feat(query-builder): Fixed date formatting in condition chips --- .../lib/query-builder/query-builder-tree.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 40cf47f9659..ab136ff651a 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -91,7 +91,7 @@
(selectedChanged)="onChipSelectionEnd()" > - {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} + {{expressionItem.fieldLabel || expressionItem.expression.field}} {{ @@ -111,17 +111,17 @@
{{ isDate(expressionItem.expression.searchVal) - ? getFormatter(expressionItem.expression.fieldName) + ? getFormatter(expressionItem.expression.field) ? (expressionItem.expression.searchVal | fieldFormatter : getFormatter( - expressionItem.expression.fieldName + expressionItem.expression.field ) : undefined) : (expressionItem.expression.searchVal | date : getFormat( - expressionItem.expression.fieldName + expressionItem.expression.field ) : undefined : this.locale) From 12559127400528d3c2fd10ed0ddec2d2e42e10da Mon Sep 17 00:00:00 2001 From: desig9stein Date: Wed, 28 Aug 2024 11:54:20 +0300 Subject: [PATCH 031/147] refactor(query-builder): rename variables --- .../query-builder/_query-builder-theme.scss | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 251fe775d98..3df874ca35d 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -30,9 +30,9 @@ $background-or--focus: null, $border-color: null, $border-radius: null, - $query-background: null, - $query-border-color: null, - $query-border-radius: null, + $tree-background: null, + $tree-border-color: null, + $tree-border-radius: null, ) { $name: 'igx-query-builder'; $selector: 'igx-query-builder'; @@ -62,9 +62,9 @@ background-or: $background-or, background-or--focus: $background-or--focus, border-color: $border-color, - query-background: $query-background, - query-border-color: $query-border-color, - query-border-radius: $query-border-radius, + tree-background: $tree-background, + tree-border-color: $tree-border-color, + tree-border-radius: $tree-border-radius, border-radius: $border-radius, _meta: map.merge(if($meta, $meta, ()), ( variant: map.get($schema, '_meta', 'theme') @@ -101,7 +101,7 @@ compact: rem(8px) ); - $backgrounds: (var-get($theme, 'background'), var-get($theme, 'query-background')); + $backgrounds: (var-get($theme, 'background'), var-get($theme, 'tree-background')); $gap: sizable( map.get($vertical-space, 'compact'), @@ -129,10 +129,10 @@ %query-builder-tree { width: 100%; - border-radius: var-get($theme, 'query-border-radius'); + border-radius: var-get($theme, 'tree-border-radius'); %query-builder-tree { - border: rem(1px) solid var-get($theme, 'query-border-color'); + border: rem(1px) solid var-get($theme, 'tree-border-color'); %filter-empty { display: none; From cd943334d9eebfff7a96e0a67bc6680e2a7f4268 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 28 Aug 2024 11:59:55 +0300 Subject: [PATCH 032/147] deps(theming): bump to latest beta containing changes --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index defce5f918e..d2765313328 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/source-map": "0.5.2", "express": "^4.19.2", "fflate": "^0.8.1", - "igniteui-theming": "^11.0.0", + "igniteui-theming": "^11.0.1-beta.1", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", @@ -12899,9 +12899,9 @@ } }, "node_modules/igniteui-theming": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/igniteui-theming/-/igniteui-theming-11.0.0.tgz", - "integrity": "sha512-t2hRgFBcS4QQFzgPwp3V8d5CFgLFWN9GY0GqnoBdTmtHQEm+2hbzrxgA//akUkO7g5Z6WEp5Y4WWaRkPJVy1HQ==", + "version": "11.0.1-beta.1", + "resolved": "https://registry.npmjs.org/igniteui-theming/-/igniteui-theming-11.0.1-beta.1.tgz", + "integrity": "sha512-WFfgWhNa+DufRQfI+Ni9d8qCFWtk4kOMsTTv6w2iq6/Jnwk2HGJSrFdCfQWJOKODkziuaJ+RfNpP6Qa/sDj9Uw==", "license": "MIT", "peerDependencies": { "sass": "^1.58.1" diff --git a/package.json b/package.json index 7ff2399b50c..27240661bf0 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@types/source-map": "0.5.2", "express": "^4.19.2", "fflate": "^0.8.1", - "igniteui-theming": "^11.0.0", + "igniteui-theming": "^11.0.1-beta.1", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", From 342cb7a020e09d33afb9957fe51ae600c44cd919 Mon Sep 17 00:00:00 2001 From: gedinakova Date: Wed, 28 Aug 2024 14:01:21 +0300 Subject: [PATCH 033/147] fix(*): Moved level calculation into a getter to avoid console exception --- .../query-builder-tree.component.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index bd35cebfc55..aee8b9ac796 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -460,7 +460,18 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { private _locale; private _entityNewValue: EntityType; private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); - public level: number = 0; + + public get level(): number { + let parent = this.elRef.nativeElement.parentElement; + let _level = 0; + while (parent) { + if (parent.localName === 'igx-query-builder-tree') { + _level++; + } + parent = parent.parentElement; + } + return _level; + } private _positionSettings = { horizontalStartPoint: HorizontalAlignment.Right, @@ -490,21 +501,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; - this.calculateNestingLevel(); // Trigger additional change detection cycle this.cdr.detectChanges(); } - private calculateNestingLevel() { - let parent = this.elRef.nativeElement.parentElement; - while (parent) { - if (parent.localName === 'igx-query-builder-tree') { - this.level++; - } - parent = parent.parentElement; - } - } - /** * @hidden @internal */ From 2c1dd932e5fe1cbb8ab3ba5b3c00565b04a1c9f9 Mon Sep 17 00:00:00 2001 From: teodosia Date: Thu, 29 Aug 2024 12:34:26 +0300 Subject: [PATCH 034/147] feat(query-builder): fix model and lint errors --- .../src/lib/grids/grid/grid-filtering-advanced.spec.ts | 2 +- .../src/lib/query-builder/query-builder-tree.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index b8753edffa5..26346970701 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -3199,7 +3199,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { //Create dateTime filtering expression const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - fieldName: 'DateTimeCreated', searchVal: '11/11/2000 10:11:11 AM', condition: IgxStringFilteringOperand.instance().condition('equals') + field: 'DateTimeCreated', searchVal: '11/11/2000 10:11:11 AM', conditionName: 'equals', condition: IgxStringFilteringOperand.instance().condition('equals') }); grid.advancedFilteringExpressionsTree = tree; diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index aee8b9ac796..b331ff01736 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -162,7 +162,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - @HostBinding('class') get getClass() { + @HostBinding('class') public get getClass() { return `igx-query-builder-tree--level-${this.level}`; } From f025bac28c347dee706eb8dd92862ece780abd99 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 29 Aug 2024 14:07:41 +0300 Subject: [PATCH 035/147] feat(query-builder): Added bindings for overlay settings where missing --- .../src/lib/query-builder/query-builder-tree.component.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index ab136ff651a..80333674018 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -423,6 +423,7 @@
@@ -440,6 +441,7 @@
[valueKey]="'field'" [disabled]="!selectedEntity" [(ngModel)]="selectedReturnFields" + [overlaySettings]="fieldSelectOverlaySettings" searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" > From 91a5719fcf014f019ffa2cf43f0128018382fa16 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 29 Aug 2024 14:44:30 +0300 Subject: [PATCH 036/147] feat(query-builder): Added checks for undefined fields --- .../query-builder-tree.component.html | 2 +- .../query-builder-tree.component.ts | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 80333674018..cf9acf29c93 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -448,7 +448,7 @@
f.field); + + this._selectedReturnFields = this._entityNewValue.fields?.map(f => f.field); + if (this.expressionTree) { this.expressionTree.entity = this._selectedEntity.name; this.expressionTree.returnFields = []; @@ -1293,8 +1298,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (entity) { this.fields = entity.fields; } - const field = this.fields.find(el => el.field === filteringExpr.field); - operandItem.fieldLabel = field.label || field.header || field.field; + const field = this.fields?.find(el => el.field === filteringExpr.field); + operandItem.fieldLabel = field?.label || field?.header || field?.field; if (this._expandedExpressions.filter(e => e.searchTree == operandItem.expression.searchTree).length > 0) { operandItem.expanded = true; } @@ -1302,8 +1307,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedEntity = this.entities.find(el => el.name === entityName); this._selectedReturnFields = !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') - ? this.fields.map(f => f.field) - : this.fields.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); + ? this.fields?.map(f => f.field) + : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } } } From 36e04c1c9d6c904a9f8b2b21281dc09f5cdc8a50 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 29 Aug 2024 14:55:06 +0300 Subject: [PATCH 037/147] feat(query-builder): Fixed return fields overlay positioning --- .../query-builder/query-builder-tree.component.html | 2 +- .../query-builder/query-builder-tree.component.ts | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index cf9acf29c93..924e083a510 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -441,7 +441,7 @@
[valueKey]="'field'" [disabled]="!selectedEntity" [(ngModel)]="selectedReturnFields" - [overlaySettings]="fieldSelectOverlaySettings" + [overlaySettings]="returnFieldSelectOverlaySettings" searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" > diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index cbf818fc20b..03aec98aa97 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -412,6 +412,15 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public pickerOutlet: IgxOverlayOutletDirective | ElementRef; + /** + * @hidden @internal + */ + public returnFieldSelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: true + }; + /** * @hidden @internal */ @@ -501,6 +510,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; + this.returnFieldSelectOverlaySettings.outlet = this.overlayOutlet; // Trigger additional change detection cycle this.cdr.detectChanges(); } @@ -870,6 +880,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.entitySelectOverlaySettings.target = this.entitySelect.element; this.entitySelectOverlaySettings.excludeFromOutsideClick = [this.entitySelect.element as HTMLElement]; this.entitySelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + this.returnFieldSelectOverlaySettings.target = this.selectedReturnFieldsCombo.getEditElement(); + this.returnFieldSelectOverlaySettings.excludeFromOutsideClick = [this.selectedReturnFieldsCombo.getEditElement() as HTMLElement]; + this.returnFieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); this.fieldSelectOverlaySettings.target = this.fieldSelect.element; this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); From 28a92c3f0c5bb9b4a333a4120ad2d2ff565333a3 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 29 Aug 2024 14:55:06 +0300 Subject: [PATCH 038/147] feat(query-builder): Fixed return fields overlay positioning --- .../query-builder/query-builder-tree.component.html | 4 ++-- .../lib/query-builder/query-builder-tree.component.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index cf9acf29c93..0148e20377c 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -136,7 +136,7 @@
expressionItem.expression.searchTree.returnFields : expressionItem.expression.searchVal ? expressionItem.expression.searchVal : - '' + null }}
[valueKey]="'field'" [disabled]="!selectedEntity" [(ngModel)]="selectedReturnFields" - [overlaySettings]="fieldSelectOverlaySettings" + [overlaySettings]="returnFieldSelectOverlaySettings" searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" > diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index cbf818fc20b..f2a2d3b96bd 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -412,6 +412,15 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public pickerOutlet: IgxOverlayOutletDirective | ElementRef; + /** + * @hidden @internal + */ + public returnFieldSelectOverlaySettings: OverlaySettings = { + scrollStrategy: new AbsoluteScrollStrategy(), + modal: false, + closeOnOutsideClick: true + }; + /** * @hidden @internal */ @@ -501,6 +510,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; this.conditionSelectOverlaySettings.outlet = this.overlayOutlet; + this.returnFieldSelectOverlaySettings.outlet = this.overlayOutlet; // Trigger additional change detection cycle this.cdr.detectChanges(); } From 17d21479c98a1d86a5e6f6902ff32607f9619f84 Mon Sep 17 00:00:00 2001 From: desig9stein Date: Thu, 29 Aug 2024 16:09:00 +0300 Subject: [PATCH 039/147] fix(query-builder): fix combo dd header styles --- .../query-builder/_query-builder-theme.scss | 4 ++++ .../query-builder-tree.component.html | 23 ++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 3df874ca35d..471a8e73909 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -124,6 +124,10 @@ background-color: var-get($theme, 'background'); border-radius: $border-radius; border: rem(1px) solid var-get($theme, 'border-color'); + + %igx-drop-down__header { + pointer-events: all; + } } %query-builder-tree { diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 0148e20377c..07602ad7714 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -446,17 +446,18 @@
[style.display]="isInEditMode() ? 'block' : 'none'" > - - - {{ this.resourceStrings.igx_query_builder_select_all }} +
+ + {{ this.resourceStrings.igx_query_builder_select_all }} + +
From def2c0e4e4afa05d19274dbf4441a1810f15940b Mon Sep 17 00:00:00 2001 From: desig9stein Date: Thu, 29 Aug 2024 16:14:00 +0300 Subject: [PATCH 040/147] fix(query-builder): use the appropriate class for the dropdown item --- .../styles/components/query-builder/_query-builder-theme.scss | 4 ---- .../src/lib/query-builder/query-builder-tree.component.html | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 471a8e73909..3df874ca35d 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -124,10 +124,6 @@ background-color: var-get($theme, 'background'); border-radius: $border-radius; border: rem(1px) solid var-get($theme, 'border-color'); - - %igx-drop-down__header { - pointer-events: all; - } } %query-builder-tree { diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 07602ad7714..56c5b4b9629 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -446,7 +446,7 @@
[style.display]="isInEditMode() ? 'block' : 'none'" > -
+
Date: Tue, 6 Aug 2024 16:23:20 +0300 Subject: [PATCH 041/147] feat(query-builder): Added request to backend on tree change --- .../query-builder/query-builder.sample.html | 5 ++++ src/app/query-builder/query-builder.sample.ts | 27 +++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 9e596150045..eee25a16282 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -4,6 +4,7 @@ @@ -14,6 +15,10 @@
--> +
+ {{ this.queryResult }} +
+
{{ printExpressionTree(queryBuilder.expressionTree) }}
diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index be2395f8251..4d8bf25b068 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -1,5 +1,6 @@ import { Component, ViewChild, OnInit } from '@angular/core'; -import { FilteringExpressionsTree, IgxStringFilteringOperand, +import { + FilteringExpressionsTree, IgxStringFilteringOperand, FilteringLogic, IgxQueryBuilderComponent, changei18n, @@ -10,6 +11,7 @@ import { FilteringExpressionsTree, IgxStringFilteringOperand, IgxQueryBuilderHeaderComponent} from 'igniteui-angular'; import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; +import { CommonModule } from '@angular/common'; @Component({ providers: [], @@ -17,7 +19,7 @@ import { SizeSelectorComponent } from '../size-selector/size-selector.component' styleUrls: ['query-builder.sample.scss'], templateUrl: 'query-builder.sample.html', standalone: true, - imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent] + imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent, CommonModule] }) export class QueryBuilderComponent implements OnInit { @ViewChild('queryBuilder', { static: true }) @@ -27,6 +29,8 @@ export class QueryBuilderComponent implements OnInit { public fields: Array; public displayDensities; public expressionTree: IExpressionTree; + public queryResult: string; + private backendUrl = "http://localhost:3333/"; public ngOnInit(): void { this.entities = [ @@ -116,6 +120,21 @@ export class QueryBuilderComponent implements OnInit { // }); this.expressionTree = tree; + this.onChange(); + } + + public async onChange() { + const tree = JSON.stringify(this.expressionTree); + const resp = await fetch(this.backendUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: tree + }) + if (resp.status == 200) { + this.queryResult = await resp.text(); + } } public changeLocale(locale: string) { @@ -126,6 +145,10 @@ export class QueryBuilderComponent implements OnInit { } public printExpressionTree(tree: IExpressionTree) { + if (JSON.stringify(tree) !== JSON.stringify(this.expressionTree)) { + this.expressionTree = tree; + this.onChange(); + } return tree ? JSON.stringify(tree, null, 2) : 'Please add an expression!'; } } From 6ef538d82478a633107487ca8124f1496d32aa07 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Mon, 2 Sep 2024 15:05:30 +0300 Subject: [PATCH 042/147] feat(query-builder): Fixed operand reset after entity switch --- .../src/lib/query-builder/query-builder-tree.component.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 03aec98aa97..3c3873a322f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -571,10 +571,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.expressionTreeChange.emit(this._expressionTree); - this._editedExpression = null; this.addAndGroup(); } + this._editedExpression = null; + this._selectedField = null; + this.selectedCondition = null; + this.searchValue = null; + this.entityChangeDialog.close(); this.entitySelect.close(); From 042c2e1511efe0da5ed7a2982f862c01a5abc44f Mon Sep 17 00:00:00 2001 From: gedinakova Date: Wed, 4 Sep 2024 15:45:57 +0300 Subject: [PATCH 043/147] fix(QB): Fixed the expressionTree's entity loss. --- .../query-builder-tree.component.ts | 10 ++++----- src/app/query-builder/query-builder.sample.ts | 22 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 3c3873a322f..dad5713c749 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1332,16 +1332,16 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { return groupItem; } - private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem): FilteringExpressionsTree { + private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem, entity?: string, returnFields?: string[]): FilteringExpressionsTree { if (!groupItem) { return null; } - const expressionTree = new FilteringExpressionsTree(groupItem.operator); + const expressionTree = new FilteringExpressionsTree(groupItem.operator, entity, returnFields); for (const item of groupItem.children) { if (item instanceof ExpressionGroupItem) { - const subTree = this.createExpressionTreeFromGroupItem((item as ExpressionGroupItem)); + const subTree = this.createExpressionTreeFromGroupItem((item as ExpressionGroupItem), entity, returnFields); expressionTree.filteringOperands.push(subTree); } else { expressionTree.filteringOperands.push((item as ExpressionOperandItem).expression); @@ -1415,9 +1415,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { children.splice(index, 1); const entity = this.expressionTree ? this.expressionTree.entity : null; const returnFields = this.expressionTree ? this.expressionTree.returnFields : null; - this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); // TODO: don't recreate if not necessary - this._expressionTree.entity = entity; - this._expressionTree.returnFields = returnFields; + this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, entity, returnFields); // TODO: don't recreate if not necessary if (!children.length) { this.deleteItem(expressionItem.parent); diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 4d8bf25b068..d241d9dbf87 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -124,17 +124,17 @@ export class QueryBuilderComponent implements OnInit { } public async onChange() { - const tree = JSON.stringify(this.expressionTree); - const resp = await fetch(this.backendUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: tree - }) - if (resp.status == 200) { - this.queryResult = await resp.text(); - } + // const tree = JSON.stringify(this.expressionTree); + // const resp = await fetch(this.backendUrl, { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: tree + // }) + // if (resp.status == 200) { + // this.queryResult = await resp.text(); + // } } public changeLocale(locale: string) { From 3a759dff2a8dc33a4c0d0ee0982042162368ebb3 Mon Sep 17 00:00:00 2001 From: gedinakova Date: Wed, 4 Sep 2024 18:12:54 +0300 Subject: [PATCH 044/147] fix(QB): Fixed the error when adding a new condition initially. --- .../src/lib/query-builder/query-builder-tree.component.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index dad5713c749..c77ba640afc 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -569,12 +569,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.expressionTree.returnFields = []; this.expressionTree.filteringOperands = []; + this._editedExpression = null; this.expressionTreeChange.emit(this._expressionTree); this.addAndGroup(); } - this._editedExpression = null; this._selectedField = null; this.selectedCondition = null; this.searchValue = null; @@ -737,11 +737,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._editedExpression = null; } - this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); - if (this._expressionTree) { - this._expressionTree.entity = this.selectedEntity.name; - this._expressionTree.returnFields = this.selectedReturnFields; - } + this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, this.selectedEntity?.name, this.selectedReturnFields); this.expressionTreeChange.emit(this._expressionTree); } From b934405312d35db6c706cc49ca3c67c79c4aef29 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 5 Sep 2024 12:03:50 +0300 Subject: [PATCH 045/147] feat(query-builder): add search value template directive, adjust adv filtering --- .../advanced-filtering-dialog.component.html | 3 +- .../src/lib/query-builder/public_api.ts | 4 +- .../query-builder-tree.component.html | 197 +++++++++--------- .../query-builder-tree.component.ts | 22 +- .../query-builder.component.html | 8 +- .../query-builder/query-builder.component.ts | 10 +- .../query-builder/query-builder.directives.ts | 26 +++ .../query-builder/query-builder.sample.html | 4 + src/app/query-builder/query-builder.sample.ts | 12 +- 9 files changed, 178 insertions(+), 108 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts diff --git a/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html b/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html index 9d6946c6726..f09c0d7b430 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html +++ b/projects/igniteui-angular/src/lib/grids/filtering/advanced-filtering/advanced-filtering-dialog.component.html @@ -4,7 +4,8 @@ 'igx-advanced-filter': true, 'igx-advanced-filter--inline': inline }"> - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + selectedField.filters.condition(selectedCondition) .isUnary) " - [igxDateTimeEditor]="selectedField.defaultDateTimeFormat" - /> - + [locale]="this.locale" + [outlet]="pickerOutlet" + [formatter]="selectedField.formatter" + [inputFormat]="selectedField.defaultTimeFormat" + > + + + + + + + + + + +
-
+
= null; + + // @ViewChildren(IgxQueryBuilderSearchValueTemplateDirective) + // public searchValueTemplateDirectives: QueryList; + @ViewChild('editingInputsContainer', { read: ElementRef }) protected set editingInputsContainer(value: ElementRef) { if ((value && !this._editingInputsContainer) || @@ -735,7 +749,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup); if (this._expressionTree) { - this._expressionTree.entity = this.selectedEntity.name; + this._expressionTree.entity = this.selectedEntity?.name; this._expressionTree.returnFields = this.selectedReturnFields; } this.expressionTreeChange.emit(this._expressionTree); @@ -781,7 +795,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { return this.selectedField && this.selectedCondition && ( - (!!this.searchValue && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || + ((!!this.searchValue || !!this.searchValueTemplate) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || (innerQuery && !!innerQuery.expressionTree) || this.selectedField.filters.condition(this.selectedCondition).isUnary ); @@ -896,7 +910,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.conditionSelect.input.nativeElement.focus(); } else { const input = this.searchValueInput?.nativeElement || this.picker?.getEditElement(); - input.focus(); + input?.focus(); } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index b27b487eb03..f9dc2026485 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -13,4 +13,10 @@ [resourceStrings]="this.resourceStrings" (expressionTreeChange)="onExpressionTreeChange($event)" > - \ No newline at end of file + + + + + + + diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 40c591dfd27..c261c271d68 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -1,5 +1,5 @@ -import { ContentChild, EventEmitter, Output } from '@angular/core'; -import { NgIf} from '@angular/common'; +import { ContentChild, EventEmitter, Output, TemplateRef } from '@angular/core'; +import { NgIf, NgTemplateOutlet} from '@angular/common'; import { Component, Input, ViewChild, ElementRef, OnDestroy, HostBinding } from '@angular/core'; @@ -13,6 +13,7 @@ import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component'; import { IgxIconService } from '../icon/icon.service'; import { editor } from '@igniteui/material-icons-extended'; +import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives'; /** * A component used for operating with complex filters by creating or editing conditions @@ -29,7 +30,7 @@ import { editor } from '@igniteui/material-icons-extended'; selector: 'igx-query-builder', templateUrl: './query-builder.component.html', standalone: true, - imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxQueryBuilderTreeComponent] + imports: [NgIf, IgxQueryBuilderHeaderComponent, IgxQueryBuilderTreeComponent, NgTemplateOutlet, IgxQueryBuilderSearchValueTemplateDirective] }) export class IgxQueryBuilderComponent implements OnDestroy { /** @@ -114,6 +115,9 @@ export class IgxQueryBuilderComponent implements OnDestroy { @ContentChild(IgxQueryBuilderHeaderComponent) public headerContent: IgxQueryBuilderHeaderComponent; + @ContentChild(IgxQueryBuilderSearchValueTemplateDirective, { read: TemplateRef }) + public searchValueTemplate: TemplateRef; + /** * @hidden @internal */ diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts new file mode 100644 index 00000000000..676f6d178d0 --- /dev/null +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts @@ -0,0 +1,26 @@ +import { Directive, Input, TemplateRef } from '@angular/core'; + +/** + * Defines the custom template that will be used for the search value input of condition in edit mode + * + * @igxModule IgxQueryBuilderModule + * @igxKeywords query builder, query builder search value + * @igxGroup Data entry and display + * + * @example + * + * + * Custom Search Value + * + * + */ +@Directive({ + selector: '[igxQueryBuilderSearchValue]', + standalone: true +}) +export class IgxQueryBuilderSearchValueTemplateDirective { + @Input() + public searchValue: any; + + constructor(public template: TemplateRef) { } +} diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index eee25a16282..60a40c4660f 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -9,6 +9,10 @@ [expressionTree]="this.expressionTree"> + + - diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 24cfcbc1aa1..c2f36f2c8ea 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -148,8 +148,10 @@ export class QueryBuilderComponent implements OnInit { this.queryBuilder.locale = locale; } - public setSearchValue() { - this.searchValueTemplate.searchValue = 'value from template'; + public logParams(field: any, condition: any, searchValue: any) { + console.log(field); + console.log(condition); + console.log(searchValue); } public printExpressionTree(tree: IExpressionTree) { From 976777c8c02260446ea65a49ed29d96a49cc0f21 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 5 Sep 2024 17:26:55 +0300 Subject: [PATCH 050/147] feat(query-builder): export directive --- projects/igniteui-angular/src/lib/query-builder/public_api.ts | 1 + .../src/lib/query-builder/query-builder-tree.component.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/public_api.ts b/projects/igniteui-angular/src/lib/query-builder/public_api.ts index 37d52d77cb4..feee1456259 100644 --- a/projects/igniteui-angular/src/lib/query-builder/public_api.ts +++ b/projects/igniteui-angular/src/lib/query-builder/public_api.ts @@ -6,6 +6,7 @@ export { IgxQueryBuilderComponent } from './query-builder.component'; export * from './query-builder-header.component'; +export * from './query-builder.directives'; /* NOTE: Query builder directives collection for ease-of-use import in standalone components scenario */ export const IGX_QUERY_BUILDER_DIRECTIVES = [ diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 00d9094eb33..f7d021f27ae 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -249,7 +249,7 @@
From 766705ea7ba85258262204a89f8f7faf22e741d3 Mon Sep 17 00:00:00 2001 From: gedinakova Date: Mon, 9 Sep 2024 16:50:08 +0300 Subject: [PATCH 051/147] fix(QB): Removed in/notIn from filtering UIs. --- .../data-operations/filtering-condition.ts | 22 ++++++++----------- .../query-builder-tree.component.ts | 13 +++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts index 4fb7e038034..5ed3e72144c 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts @@ -19,16 +19,6 @@ export class IgxFilteringOperand { isUnary: true, iconName: 'filter_not_null', logic: (target: any) => target !== null - }, { - name: 'in', - isUnary: false, - iconName: 'in', - logic: (target: any, searchVal: Set) => this.findValueInSet(target, searchVal) - }, { - name: 'notIn', - isUnary: false, - iconName: 'not-in', - logic: (target: any, searchVal: Set) => !this.findValueInSet(target, searchVal) }]; } @@ -64,7 +54,7 @@ export class IgxFilteringOperand { /** * @hidden */ - protected findValueInSet(target: any, searchVal: Set) { + public findValueInSet(target: any, searchVal: Set) { return searchVal.has(target); } } @@ -170,7 +160,10 @@ class IgxBaseDateTimeFilteringOperand extends IgxFilteringOperand { return res; } - protected override findValueInSet(target: any, searchVal: Set) { + /** + * @hidden + */ + public override findValueInSet(target: any, searchVal: Set) { if (!target) { return false; } @@ -708,7 +701,10 @@ export class IgxTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand { }].concat(this.operations); } - protected override findValueInSet(target: any, searchVal: Set) { + /** + * @hidden + */ + public override findValueInSet(target: any, searchVal: Set) { if (!target) { return false; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 74a09a2e163..59964ca13ca 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1258,6 +1258,19 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { break; } + field.filters.append({ + name: 'in', + isUnary: false, + iconName: 'in', + logic: (target: any, searchVal: Set) => field.filters.findValueInSet(target, searchVal) + }); + + field.filters.append({ + name: 'notIn', + isUnary: false, + iconName: 'not-in', + logic: (target: any, searchVal: Set) => !field.filters.findValueInSet(target, searchVal) + }); } } From 82b6c7d8cda8c8b3f4ab52e494b343f7e0502249 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 10 Sep 2024 14:34:05 +0300 Subject: [PATCH 052/147] feat(query-builder): handling setting search value from template --- .../lib/grids/filtering/grid-filtering.service.ts | 2 +- .../src/lib/query-builder/public_api.ts | 2 +- .../query-builder-tree.component.html | 2 +- .../query-builder/query-builder-tree.component.ts | 14 +++++++++++--- src/app/query-builder/query-builder.sample.html | 4 ++-- src/app/query-builder/query-builder.sample.ts | 9 ++------- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts index 02c1c81338e..94c386742de 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts @@ -543,7 +543,7 @@ export class IgxFilteringService implements OnDestroy { } if (condition) { - const newExpression: IFilteringExpression = { field: fieldName, searchVal, condition, conditionName: condition.name, ignoreCase }; + const newExpression: IFilteringExpression = { field: fieldName, searchVal: searchVal, condition, conditionName: condition.name, ignoreCase }; expressionsTree = new FilteringExpressionsTree(filteringState.operator, fieldName); expressionsTree.filteringOperands.push(newExpression); } diff --git a/projects/igniteui-angular/src/lib/query-builder/public_api.ts b/projects/igniteui-angular/src/lib/query-builder/public_api.ts index feee1456259..b711eedcd59 100644 --- a/projects/igniteui-angular/src/lib/query-builder/public_api.ts +++ b/projects/igniteui-angular/src/lib/query-builder/public_api.ts @@ -12,5 +12,5 @@ export * from './query-builder.directives'; export const IGX_QUERY_BUILDER_DIRECTIVES = [ IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, - IgxQueryBuilderSearchValueTemplateDirective + IgxQueryBuilderSearchValueTemplateDirective, ] as const; diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index f7d021f27ae..1fbb58f5a73 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -249,7 +249,7 @@
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 74a09a2e163..384f3417bb6 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -719,10 +719,11 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public commitOperandEdit() { + const actualSearchValue = this.searchValueTemplate ? this._editedExpression.expression.searchVal : this.searchValue; if (this._editedExpression) { this._editedExpression.expression.field = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); - this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); // + this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, actualSearchValue); this._editedExpression.fieldLabel = this.selectedField.label ? this.selectedField.label : this.selectedField.header @@ -789,10 +790,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public operandCanBeCommitted(): boolean { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; - return this.selectedField && this.selectedCondition && ( - ((!!this.searchValue || !!this.searchValueTemplate) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || + ((!!this.searchValue || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || (innerQuery && !!innerQuery.expressionTree) || this.selectedField.filters.condition(this.selectedCondition).isUnary ); @@ -1212,6 +1212,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } + public getSearchValueTemplateContext(): any { + const ctx = { + $implicit: this.searchValue, + expression: this._editedExpression.expression + }; + return ctx; + } + private setFormat(field: FieldType) { if (!field.pipeArgs) { field.pipeArgs = { digitsInfo: DEFAULT_PIPE_DIGITS_INFO }; diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 98e38e3aa57..99d00e5833c 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -10,8 +10,8 @@ - diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index c2f36f2c8ea..a0ac6f3016d 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -13,6 +13,7 @@ import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; import { CommonModule } from '@angular/common'; import { IgxQueryBuilderSearchValueTemplateDirective } from 'igniteui-angular/src/lib/query-builder/query-builder.directives'; +import { FormsModule } from '@angular/forms'; @Component({ providers: [], @@ -20,7 +21,7 @@ import { IgxQueryBuilderSearchValueTemplateDirective } from 'igniteui-angular/sr styleUrls: ['query-builder.sample.scss'], templateUrl: 'query-builder.sample.html', standalone: true, - imports: [IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent, CommonModule, IgxQueryBuilderSearchValueTemplateDirective] + imports: [FormsModule, IgxButtonGroupComponent, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxButtonDirective, IgxRippleDirective, SizeSelectorComponent, CommonModule, IgxQueryBuilderSearchValueTemplateDirective] }) export class QueryBuilderComponent implements OnInit { @ViewChild('queryBuilder', { static: true }) @@ -148,12 +149,6 @@ export class QueryBuilderComponent implements OnInit { this.queryBuilder.locale = locale; } - public logParams(field: any, condition: any, searchValue: any) { - console.log(field); - console.log(condition); - console.log(searchValue); - } - public printExpressionTree(tree: IExpressionTree) { if (JSON.stringify(tree) !== JSON.stringify(this.expressionTree)) { this.expressionTree = tree; From a4f22e97763d27b3c4ed17f031772e40f01b958c Mon Sep 17 00:00:00 2001 From: gedinakova Date: Tue, 10 Sep 2024 14:37:16 +0300 Subject: [PATCH 053/147] fix(QB): Removed in/not in from filtering UI properly. --- .../data-operations/filtering-condition.ts | 20 ++++++++++++++++- .../query-builder-tree.component.ts | 22 +++++-------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts index 5ed3e72144c..0809e7e7288 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts @@ -19,6 +19,17 @@ export class IgxFilteringOperand { isUnary: true, iconName: 'filter_not_null', logic: (target: any) => target !== null + }, { + name: 'in', + isUnary: false, + iconName: 'in', + logic: (target: any, searchVal: Set) => this.findValueInSet(target, searchVal) + }, + { + name: 'notIn', + isUnary: false, + iconName: 'not-in', + logic: (target: any, searchVal: Set) => !this.findValueInSet(target, searchVal) }]; } @@ -27,9 +38,16 @@ export class IgxFilteringOperand { } /** - * Returns an array of names of the conditions which are visible in the UI + * Returns an array of names of the conditions which are visible in the filtering UI */ public conditionList(): string[] { + return this.extendedConditionList().filter(c => c !== 'in' && c !== 'notIn'); + } + + /** + * Returns an array of names of the conditions which are visible in the UI, including "In" and "Not In", allowing the creation of sub-queries. + */ + public extendedConditionList(): string[] { return this.operations.filter(f => !f.hidden).map((element) => element.name); } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 59964ca13ca..60b0f404898 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1104,11 +1104,13 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public getConditionList(): string[] { - let conditionList = this.selectedField ? this.selectedField.filters.conditionList() : []; + if (!this.selectedField) return []; + if (this.entities.length === 1 && !this.entities[0].name) { - conditionList = conditionList.filter(c => c !== 'in' && c !== 'notIn') + return this.selectedField.filters.conditionList(); } - return conditionList; + + return this.selectedField.filters.extendedConditionList(); } /** @@ -1257,20 +1259,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { field.filters = IgxStringFilteringOperand.instance(); break; } - - field.filters.append({ - name: 'in', - isUnary: false, - iconName: 'in', - logic: (target: any, searchVal: Set) => field.filters.findValueInSet(target, searchVal) - }); - - field.filters.append({ - name: 'notIn', - isUnary: false, - iconName: 'not-in', - logic: (target: any, searchVal: Set) => !field.filters.findValueInSet(target, searchVal) - }); } } From 6c1802889223a2ef372632903e38491da4555a81 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Wed, 11 Sep 2024 12:57:46 +0300 Subject: [PATCH 054/147] fix(query-builder): Removed unused refs --- .../src/lib/query-builder/query-builder-tree.component.ts | 2 -- src/app/query-builder/query-builder.sample.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 023cc3e57ef..4825dfe77f4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1,9 +1,7 @@ import { AfterViewInit, ContentChild, - ContentChildren, EventEmitter, - forwardRef, LOCALE_ID, Output, Pipe, diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index a0ac6f3016d..4b1e7fca3a2 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild, OnInit, TemplateRef } from '@angular/core'; +import { Component, ViewChild, OnInit } from '@angular/core'; import { FilteringExpressionsTree, IgxStringFilteringOperand, FilteringLogic, From 6dc907e3b9743c2e61d4e3d3b819ffbc99370aff Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 12 Sep 2024 15:20:30 +0300 Subject: [PATCH 055/147] feat(query-builder): fix new group options and improve emit on change --- .../query-builder-tree.component.ts | 24 ++++++++----------- .../query-builder/query-builder.component.ts | 21 ++++++++++++++-- .../query-builder/query-builder.directives.ts | 4 ++-- .../query-builder/query-builder.sample.html | 2 +- src/app/query-builder/query-builder.sample.ts | 10 +++++--- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 023cc3e57ef..75422c5c9df 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -228,8 +228,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedEntity = null; this._selectedReturnFields = []; } - - this.init(); } /** @@ -517,6 +515,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public ngAfterViewInit(): void { + this.clearSelection(); + this.cancelOperandAdd(); + this.cancelOperandEdit(); + this.rootGroup = this.createExpressionGroupItem(this.expressionTree); + this.currentGroup = this.rootGroup; + this._overlaySettings.outlet = this.overlayOutlet; this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; @@ -575,10 +579,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedReturnFields = this._entityNewValue.fields?.map(f => f.field); - if (this.expressionTree) { - this.expressionTree.entity = this._selectedEntity.name; - this.expressionTree.returnFields = []; - this.expressionTree.filteringOperands = []; + if (this._expressionTree) { + this._expressionTree.entity = this._selectedEntity.name; + this._expressionTree.returnFields = []; + this._expressionTree.filteringOperands = []; this._editedExpression = null; this.expressionTreeChange.emit(this._expressionTree); @@ -1536,13 +1540,5 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { container.scrollTop = targetOffset + target.offsetHeight + delta - container.clientHeight; } } - - private init() { - this.clearSelection(); - this.cancelOperandAdd(); - this.cancelOperandEdit(); - this.rootGroup = this.createExpressionGroupItem(this.expressionTree); - this.currentGroup = this.rootGroup; - } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index c261c271d68..a4e093e2f1b 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -72,9 +72,22 @@ export class IgxQueryBuilderComponent implements OnDestroy { // } // } + /** + * Returns the expression tree. + */ + public get expressionTree(): IExpressionTree { + return this._expressionTree; + } + /** + * Sets the expression tree. + */ @Input() - public expressionTree: IExpressionTree; + public set expressionTree(expressionTree: IExpressionTree) { + if (JSON.stringify(expressionTree) !== JSON.stringify(this._expressionTree)) { + this._expressionTree = expressionTree; + } + } /** * Gets the `locale` of the query builder. @@ -115,6 +128,9 @@ export class IgxQueryBuilderComponent implements OnDestroy { @ContentChild(IgxQueryBuilderHeaderComponent) public headerContent: IgxQueryBuilderHeaderComponent; + /** + * @hidden @internal + */ @ContentChild(IgxQueryBuilderSearchValueTemplateDirective, { read: TemplateRef }) public searchValueTemplate: TemplateRef; @@ -126,6 +142,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { private destroy$ = new Subject(); private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); + private _expressionTree: IExpressionTree; constructor(protected iconService: IgxIconService) { this.registerSVGIcons(); @@ -183,7 +200,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { } public onExpressionTreeChange(tree: IExpressionTree) { - this.expressionTree = tree; + this._expressionTree = tree; this.expressionTreeChange.emit(); } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts index 9f1ce3a3a1a..c2cf0ea7ce9 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.directives.ts @@ -9,8 +9,8 @@ import { Directive, TemplateRef } from '@angular/core'; * * @example * - * - * {{field.field}} {{condition}} {{searchValue}} + * + * * * */ diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 99d00e5833c..025ae2d3deb 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -4,7 +4,7 @@ diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index a0ac6f3016d..412d6a40b3b 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -125,7 +125,11 @@ export class QueryBuilderComponent implements OnInit { // }); this.expressionTree = tree; - this.onChange(); + // this.onChange(); + } + + public handleExpressionTreeChange(event: any) { + console.log(this.queryBuilder.expressionTree); } public async onChange() { @@ -151,8 +155,8 @@ export class QueryBuilderComponent implements OnInit { public printExpressionTree(tree: IExpressionTree) { if (JSON.stringify(tree) !== JSON.stringify(this.expressionTree)) { - this.expressionTree = tree; - this.onChange(); + // this.expressionTree = tree; + // this.onChange(); } return tree ? JSON.stringify(tree, null, 2) : 'Please add an expression!'; } From b073b2bfe2e05d7b62783ac1517c5af8d886a105 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 12 Sep 2024 15:21:05 +0300 Subject: [PATCH 056/147] feat(query-builder): show condition name in tooltip on unary conditions --- .../src/lib/query-builder/query-builder-tree.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 1fbb58f5a73..b0c8f445cea 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -136,7 +136,7 @@
expressionItem.expression.searchTree.returnFields : expressionItem.expression.searchVal ? expressionItem.expression.searchVal : - null + getConditionFriendlyName(expressionItem.expression.condition.name) }}
Date: Thu, 12 Sep 2024 15:43:28 +0300 Subject: [PATCH 057/147] chore(*): fix tooltip on unary condition --- .../src/lib/query-builder/query-builder-tree.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index b0c8f445cea..927a3ddf7e1 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -134,9 +134,9 @@
{{ expressionItem.expression.searchTree ? expressionItem.expression.searchTree.returnFields : - expressionItem.expression.searchVal ? - expressionItem.expression.searchVal : - getConditionFriendlyName(expressionItem.expression.condition.name) + expressionItem.expression.condition.isUnary ? + getConditionFriendlyName(expressionItem.expression.condition.name) : + expressionItem.expression.searchVal }}
Date: Fri, 13 Sep 2024 12:23:17 +0300 Subject: [PATCH 058/147] feat(query-builder): support fields input --- .../query-builder-tree.component.ts | 4 ++ .../query-builder/query-builder.component.ts | 47 +++++++++---------- .../query-builder/query-builder.sample.html | 2 +- src/app/query-builder/query-builder.sample.ts | 35 ++++++++------ 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index afc1dd026e2..8184eac6676 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -201,6 +201,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public set fields(fields: FieldType[]) { this._fields = fields; + if (!this._fields && this.entities.length === 1 && !this.entities[0].name) { + this._fields = this.entities[0].fields; + } + if (this._fields) { this._fields.forEach(field => { this.setFilters(field); diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index a4e093e2f1b..4eee1e91a74 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -7,7 +7,7 @@ import { Subject } from 'rxjs'; import { IQueryBuilderResourceStrings, QueryBuilderResourceStringsEN } from '../core/i18n/query-builder-resources'; import { IExpressionTree } from '../data-operations/filtering-expressions-tree'; import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive'; -import { EntityType } from '../grids/common/grid.interface'; +import { EntityType, FieldType } from '../grids/common/grid.interface'; import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component'; import { getCurrentResourceStrings } from '../core/i18n/resources'; import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component'; @@ -48,29 +48,27 @@ export class IgxQueryBuilderComponent implements OnDestroy { @Input() public entities: EntityType[]; - // /** - // * Returns the fields. - // */ - // public get fields(): FieldType[] { - // return this._fields; - // } - - // /** - // * Sets the fields. - // */ - // @Input() - // public set fields(fields: FieldType[]) { - // this._fields = fields; - - // if (this._fields) { - // this.registerSVGIcons(); - - // this._fields.forEach(field => { - // this.setFilters(field); - // this.setFormat(field); - // }); - // } - // } + /** + * Returns the fields. + */ + public get fields(): FieldType[] { + return this._fields; + } + + /** + * Sets the fields. + */ + @Input() + public set fields(fields: FieldType[]) { + if (fields) { + this.entities = [ + { + name: null, + fields: fields + } + ]; + } + } /** * Returns the expression tree. @@ -143,6 +141,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { private destroy$ = new Subject(); private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN); private _expressionTree: IExpressionTree; + private _fields: FieldType[]; constructor(protected iconService: IgxIconService) { this.registerSVGIcons(); diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 025ae2d3deb..afa0e703adc 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -4,7 +4,7 @@ diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 80d0bf4f4ad..96ee0d71929 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -31,30 +31,35 @@ export class QueryBuilderComponent implements OnInit { public searchValueTemplate: IgxQueryBuilderSearchValueTemplateDirective; public entities: Array; - public fields: Array; + public assaysFields: Array; + public compoundsFields: Array; public displayDensities; public expressionTree: IExpressionTree; public queryResult: string; private backendUrl = "http://localhost:3333/"; public ngOnInit(): void { + this.assaysFields = [ + { field: 'Id', dataType: 'number' }, + { field: 'CompoundId', dataType: 'number' }, + { field: 'Name', dataType: 'string' }, + { field: 'EndpointName', dataType: 'string' }, + { field: 'EndpointValue', dataType: 'string' }, + { field: 'Date', dataType: 'date' } + ]; + this.compoundsFields = [ + { field: 'Id', dataType: 'number' }, + { field: 'Structure', dataType: 'string' }, + { field: 'Date', dataType: 'date' } + ]; this.entities = [ { - name: 'Assays', fields: [ - { field: 'Id', dataType: 'number' }, - { field: 'CompoundId', dataType: 'number' }, - { field: 'Name', dataType: 'string' }, - { field: 'EndpointName', dataType: 'string' }, - { field: 'EndpointValue', dataType: 'string' }, - { field: 'Date', dataType: 'date' } - ] + name: 'Assays', + fields: this.assaysFields }, { - name: 'Compounds', fields: [ - { field: 'Id', dataType: 'number' }, - { field: 'Structure', dataType: 'string' }, - { field: 'Date', dataType: 'date' } - ] + name: 'Compounds', + fields: this.compoundsFields } ]; @@ -128,7 +133,7 @@ export class QueryBuilderComponent implements OnInit { // this.onChange(); } - public handleExpressionTreeChange(event: any) { + public handleExpressionTreeChange() { console.log(this.queryBuilder.expressionTree); } From 022e8d848be31189bb0176853ffc114b7d3bc289 Mon Sep 17 00:00:00 2001 From: Galina Edinakova Date: Fri, 13 Sep 2024 18:33:22 +0300 Subject: [PATCH 059/147] fix(QB): Added guards for this.entities. --- .../query-builder/query-builder-tree.component.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 8184eac6676..3682775b465 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -187,7 +187,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * Returns the fields. */ public get fields(): FieldType[] { - if (!this._fields && this.entities.length === 1 && !this.entities[0].name) { + if (!this._fields && this.entities?.length === 1 && !this.entities[0]?.name) { this._fields = this.entities[0].fields; } @@ -201,7 +201,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public set fields(fields: FieldType[]) { this._fields = fields; - if (!this._fields && this.entities.length === 1 && !this.entities[0].name) { + if (!this._fields && this.entities?.length === 1 && !this.entities[0]?.name) { this._fields = this.entities[0].fields; } @@ -1112,7 +1112,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public getConditionList(): string[] { if (!this.selectedField) return []; - if (this.entities.length === 1 && !this.entities[0].name) { + if (this.entities?.length === 1 && !this.entities[0].name) { return this.selectedField.filters.conditionList(); } @@ -1189,7 +1189,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public formatReturnFields(innerTree: IFilteringExpressionsTree) { const returnFields = innerTree.returnFields; let text = returnFields.join(', '); - const innerTreeEntity = this.entities.find(el => el.name === innerTree.entity); + const innerTreeEntity = this.entities?.find(el => el.name === innerTree.entity); if (returnFields.length === innerTreeEntity.fields.length) { text = this.resourceStrings.igx_query_builder_all_fields; } else { @@ -1337,7 +1337,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (expressionTree.entity) { entityName = expressionTree.entity; } - const entity = this.entities.find(el => el.name === entityName); + const entity = this.entities?.find(el => el.name === entityName); if (entity) { this.fields = entity.fields; } @@ -1347,7 +1347,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { operandItem.expanded = true; } groupItem.children.push(operandItem); - this._selectedEntity = this.entities.find(el => el.name === entityName); + this._selectedEntity = this.entities?.find(el => el.name === entityName); this._selectedReturnFields = !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') ? this.fields?.map(f => f.field) From 1930b4f734ac5e0defc6aa57053e3671ed2e16fb Mon Sep 17 00:00:00 2001 From: desig9stein Date: Mon, 16 Sep 2024 19:04:29 +0300 Subject: [PATCH 060/147] fix(combo): select all item colors --- .../query-builder/_query-builder-theme.scss | 7 +++++++ .../query-builder-tree.component.html | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 3df874ca35d..6a210a68e25 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -124,6 +124,13 @@ background-color: var-get($theme, 'background'); border-radius: $border-radius; border: rem(1px) solid var-get($theme, 'border-color'); + + .igx-drop-down__item--query-builder { + --component-size: var(--ig-size); + + // This values are taken form the schema of the dropdown + height: sizable(rem(28px), rem(32px), rem(40px)); + } } %query-builder-tree { diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 927a3ddf7e1..416d50b9fc4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -246,7 +246,7 @@
- [(ngModel)]="searchValue" /> - + - + - + [style.display]="isInEditMode() ? 'block' : 'none'" > -
+
- {{ this.resourceStrings.igx_query_builder_select_all }} +
+ {{ this.resourceStrings.igx_query_builder_select_all }} +
From 08fb13b4c7dc57488d132b2c715e1ad62085a843 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Tue, 17 Sep 2024 15:00:02 +0300 Subject: [PATCH 061/147] feat(query-builder): returned init function in expressionTree setter --- .../query-builder-tree.component.ts | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 3682775b465..ee6723638bf 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -225,10 +225,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ @Input() public set expressionTree(expressionTree: IExpressionTree) { - this._expressionTree = expressionTree; - if (!expressionTree) { - this._selectedEntity = null; - this._selectedReturnFields = []; + if (JSON.stringify(expressionTree) !== JSON.stringify(this._expressionTree)) { + this._expressionTree = expressionTree; + if (!expressionTree) { + this._selectedEntity = null; + this._selectedReturnFields = []; + } + + this.init(); } } @@ -517,12 +521,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public ngAfterViewInit(): void { - this.clearSelection(); - this.cancelOperandAdd(); - this.cancelOperandEdit(); - this.rootGroup = this.createExpressionGroupItem(this.expressionTree); - this.currentGroup = this.rootGroup; - this._overlaySettings.outlet = this.overlayOutlet; this.entitySelectOverlaySettings.outlet = this.overlayOutlet; this.fieldSelectOverlaySettings.outlet = this.overlayOutlet; @@ -1542,5 +1540,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { container.scrollTop = targetOffset + target.offsetHeight + delta - container.clientHeight; } } + + + private init() { + this.clearSelection(); + this.cancelOperandAdd(); + this.cancelOperandEdit(); + this.rootGroup = this.createExpressionGroupItem(this.expressionTree); + this.currentGroup = this.rootGroup; + } } From 19c137df832bf1ff57e0d7768987c2400090057f Mon Sep 17 00:00:00 2001 From: desig9stein Date: Wed, 18 Sep 2024 12:14:10 +0300 Subject: [PATCH 062/147] refactor(query-builder): clean the scss --- .../components/query-builder/_query-builder-theme.scss | 7 ------- 1 file changed, 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 6a210a68e25..3df874ca35d 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -124,13 +124,6 @@ background-color: var-get($theme, 'background'); border-radius: $border-radius; border: rem(1px) solid var-get($theme, 'border-color'); - - .igx-drop-down__item--query-builder { - --component-size: var(--ig-size); - - // This values are taken form the schema of the dropdown - height: sizable(rem(28px), rem(32px), rem(40px)); - } } %query-builder-tree { From 9f505aae0732f264d5c8a89496d949c30e14947b Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 19 Sep 2024 10:53:17 +0300 Subject: [PATCH 063/147] fix(query-builder): Fixed failing tests due to field rename and constructor --- .../src/lib/data-operations/data-util.spec.ts | 18 +- .../filtering-expression.interface.ts | 2 +- .../filtering-expressions-tree.ts | 14 +- .../filtering-strategy.spec.ts | 15 +- .../lib/data-operations/filtering-strategy.ts | 4 +- .../base/grid-filtering-row.component.ts | 2 +- .../excel-style-custom-dialog.component.ts | 6 +- .../excel-style-filtering.component.ts | 4 +- .../excel-style-search.component.ts | 6 +- .../grids/filtering/grid-filtering.service.ts | 8 +- .../src/lib/grids/grid-base.directive.ts | 2 +- .../grid/grid-filtering-advanced.spec.ts | 237 ++++++++++++------ .../lib/grids/grid/grid-filtering-ui.spec.ts | 155 ++++++------ .../src/lib/grids/grid/grid-filtering.spec.ts | 51 ++-- .../lib/grids/grid/grid-row-selection.spec.ts | 6 +- .../src/lib/grids/grid/grid.component.spec.ts | 3 +- .../hierarchical-grid.virtualization.spec.ts | 3 +- .../lib/grids/pivot-grid/pivot-grid.spec.ts | 9 +- .../src/lib/grids/state-base.directive.ts | 10 +- .../src/lib/grids/state.directive.spec.ts | 5 +- .../lib/grids/state.hierarchicalgrid.spec.ts | 5 +- .../src/lib/grids/state.treegrid.spec.ts | 5 +- .../grids/summaries/grid-summary.service.ts | 2 +- ...id-toolbar-advanced-filtering.component.ts | 2 +- .../tree-grid/tree-grid-selection.spec.ts | 36 ++- .../query-builder-tree.component.ts | 14 +- .../services/csv/csv-exporter-grid.spec.ts | 15 +- .../excel/excel-exporter-grid.spec.ts | 15 +- .../src/lib/test-utils/grid-samples.spec.ts | 6 +- .../grid-external-filtering.sample.ts | 8 +- .../grid-filtering/grid-filtering.sample.ts | 8 +- src/app/pivot-grid/pivot-grid.sample.ts | 2 +- src/app/query-builder/query-builder.sample.ts | 16 +- 33 files changed, 422 insertions(+), 272 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts index a2104839e2d..f6adde08f78 100644 --- a/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/data-util.spec.ts @@ -350,8 +350,9 @@ const testFilter = () => { }; state.expressionsTree.filteringOperands = [ { - field: 'number', + fieldName: 'number', condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', searchVal: 3 } ]; @@ -367,7 +368,8 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - field: 'string', + conditionName: 'contains', + fieldName: 'string', searchVal: 'row' } ]; @@ -378,7 +380,8 @@ const testFilter = () => { stateIgnoreCase.expressionsTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - field: 'string', + conditionName: 'contains', + fieldName: 'string', ignoreCase: false, searchVal: 'ROW' } @@ -401,7 +404,8 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxDateFilteringOperand.instance().condition('after'), - field: 'date', + conditionName: 'after', + fieldName: 'date', searchVal: new Date() } ]; @@ -416,7 +420,8 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxBooleanFilteringOperand.instance().condition('false'), - field: 'boolean' + conditionName: 'false', + fieldName: 'boolean' } ]; const res = FilterUtil.filter(data, state); @@ -431,7 +436,8 @@ const testFilter = () => { state.expressionsTree.filteringOperands = [ { condition: IgxBooleanFilteringOperand.instance().condition('false'), - field: 'boolean' + conditionName: 'false', + fieldName: 'boolean' } ]; const res = FilterUtil.filter(data, state); diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index 3770b6f8a76..3b74491b0f5 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -14,7 +14,7 @@ export enum FilteringLogic { * Represents filtering expressions. */ export declare interface IFilteringExpression { - field: string; + fieldName: string; condition?: IFilteringOperation; conditionName: string; searchVal?: Serializable; diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts index 7aecdd01ca2..09b7cee7bf3 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts @@ -10,7 +10,7 @@ export enum FilteringExpressionsTreeType { export declare interface IExpressionTree { filteringOperands: (IExpressionTree | IFilteringExpression)[]; operator: FilteringLogic; - field?: string; + fieldName?: string; entity?: string; returnFields?: string[]; } @@ -75,7 +75,7 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { * * @memberof FilteringExpressionsTree */ - public field?: string; + public fieldName?: string; /* alternateName: treeType */ /** @@ -95,11 +95,11 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { public returnFields?: string[]; - constructor(operator: FilteringLogic, entity?: string, returnFields?: string[]) { + constructor(operator: FilteringLogic, fieldName?: string, entity?: string, returnFields?: string[]) { this.operator = operator; this.entity = entity; this.returnFields = returnFields; - // this.fieldName = fieldName; + this.fieldName = fieldName; } @@ -147,7 +147,7 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { return i; } } else { - if ((expr as IFilteringExpression).field === fieldName) { + if ((expr as IFilteringExpression).fieldName === fieldName) { return i; } } @@ -157,14 +157,14 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { } protected isFilteringExpressionsTreeForColumn(expressionsTree: IFilteringExpressionsTree, fieldName: string): boolean { - if (expressionsTree.field === fieldName) { + if (expressionsTree.fieldName === fieldName) { return true; } for (const expr of expressionsTree.filteringOperands) { if ((expr instanceof FilteringExpressionsTree)) { return this.isFilteringExpressionsTreeForColumn(expr, fieldName); - } else if ((expr as IFilteringExpression).field === fieldName) { + } else if ((expr as IFilteringExpression).fieldName === fieldName) { return true; } } diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts index 7ebc7ca7791..2ad1908e022 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.spec.ts @@ -19,7 +19,8 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - field: 'number', + conditionName: 'greaterThan', + fieldName: 'number', searchVal: 1 } ]; @@ -33,13 +34,15 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - field: 'string', + conditionName: 'contains', + fieldName: 'string', ignoreCase: false, searchVal: 'ROW' }, { condition: IgxNumberFilteringOperand.instance().condition('lessThan'), - field: 'number', + conditionName: 'lessThan', + fieldName: 'number', searchVal: 1 } ]; @@ -50,7 +53,8 @@ describe('Unit testing FilteringStrategy', () => { const rec = data[0]; const res = fs.findMatchByExpression(rec, { condition: IgxBooleanFilteringOperand.instance().condition('false'), - field: 'boolean' + conditionName: 'false', + fieldName: 'boolean' }); expect(res).toBeTruthy(); }); @@ -61,7 +65,8 @@ describe('Unit testing FilteringStrategy', () => { expressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('contains'), - field: 'string', + conditionName: 'contains', + fieldName: 'string', searchVal: 'ROW' } ]; diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts index 7ee6c3b4cfb..9674032ee56 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts @@ -39,7 +39,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { // protected public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean { const cond = expr.condition; - const val = this.getFieldValue(rec, expr.field, isDate, isTime, grid); + const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid); return cond.logic(val, expr.searchVal, expr.ignoreCase); } @@ -72,7 +72,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy { return true; } else { const expression = expressions as IFilteringExpression; - const column = grid && grid.getColumnByName(expression.field); + const column = grid && grid.getColumnByName(expression.fieldName); const isDate = column ? column.dataType === DateType || column.dataType === DateTimeType : false; const isTime = column ? column.dataType === TimeType : false; return this.findMatchByExpression(rec, expression, isDate, isTime, grid); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts index 18b426abe02..5d51d40d7b8 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/base/grid-filtering-row.component.ts @@ -847,7 +847,7 @@ export class IgxGridFilteringRowComponent implements AfterViewInit, OnDestroy { private resetExpression(condition?: string) { this.expression = { - field: this.column.field, + fieldName: this.column.field, condition: null, conditionName: null, searchVal: null, diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts index 9c573e23c5e..f85c05380b0 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-custom-dialog.component.ts @@ -168,7 +168,7 @@ export class IgxExcelStyleCustomDialogComponent implements AfterViewInit { exprUI.expression = { condition: null, conditionName: null, - field: this.column.field, + fieldName: this.column.field, ignoreCase: this.column.filteringIgnoreCase, searchVal: null }; @@ -255,7 +255,7 @@ export class IgxExcelStyleCustomDialogComponent implements AfterViewInit { firstExprUI.expression = { condition: cond, conditionName: cond.name, - field: this.column.field, + fieldName: this.column.field, ignoreCase: this.column.filteringIgnoreCase, searchVal: null }; @@ -268,7 +268,7 @@ export class IgxExcelStyleCustomDialogComponent implements AfterViewInit { secondExprUI.expression = { condition: null, conditionName: null, - field: this.column.field, + fieldName: this.column.field, ignoreCase: this.column.filteringIgnoreCase, searchVal: null }; diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts index 19ded5791b0..afa77309a76 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-filtering.component.ts @@ -619,12 +619,12 @@ export class IgxGridExcelStyleFilteringComponent extends BaseFilteringComponent private getColumnFilterExpressionsTree() { const gridExpressionsTree: IFilteringExpressionsTree = this.grid.filteringExpressionsTree; - const expressionsTree = new FilteringExpressionsTree(gridExpressionsTree.operator, gridExpressionsTree.field); + const expressionsTree = new FilteringExpressionsTree(gridExpressionsTree.operator, gridExpressionsTree.fieldName); for (const operand of gridExpressionsTree.filteringOperands) { if (operand instanceof FilteringExpressionsTree) { const columnExprTree = operand as FilteringExpressionsTree; - if (columnExprTree.field === this.column.field) { + if (columnExprTree.fieldName === this.column.field) { continue; } } diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts index 4a487872124..6346a023bf6 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/excel-style-search.component.ts @@ -570,7 +570,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { filterTree.filteringOperands.push({ condition, conditionName: condition.name, - field: this.esf.column.field, + fieldName: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, searchVal: element.value }); @@ -585,7 +585,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { filterTree.filteringOperands.push({ condition: this.createCondition('in'), conditionName: 'in', - field: this.esf.column.field, + fieldName: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, searchVal: new Set(this.esf.column.dataType === GridColumnDataType.Date || this.esf.column.dataType === GridColumnDataType.DateTime ? @@ -598,7 +598,7 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy { filterTree.filteringOperands.push({ condition: this.createCondition('empty'), conditionName: 'empty', - field: this.esf.column.field, + fieldName: this.esf.column.field, ignoreCase: this.esf.column.filteringIgnoreCase, searchVal: blanksItem.value }); diff --git a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts index 6e2d80e78c7..ef6abe0fa5b 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/grid-filtering.service.ts @@ -293,7 +293,7 @@ export class IgxFilteringService implements OnDestroy { } const filteringTree = this.grid.filteringExpressionsTree; - const newFilteringTree = new FilteringExpressionsTree(filteringTree.operator, filteringTree.field); + const newFilteringTree = new FilteringExpressionsTree(filteringTree.operator, filteringTree.fieldName); for (const column of this.grid.columns) { this.prepare_filtering_expression(newFilteringTree, column.field, value, condition, @@ -463,7 +463,7 @@ export class IgxFilteringService implements OnDestroy { if (expression.condition.isUnary) { return this.grid.resourceStrings[`igx_grid_filter_${expression.condition.name}`] || expression.condition.name; } else if (expression.searchVal instanceof Date) { - const column = this.grid.getColumnByName(expression.field); + const column = this.grid.getColumnByName(expression.fieldName); const formatter = column.formatter; if (formatter) { return formatter(expression.searchVal, undefined); @@ -541,12 +541,12 @@ export class IgxFilteringService implements OnDestroy { let newExpressionsTree = filteringState as FilteringExpressionsTree; if (createNewTree) { - newExpressionsTree = new FilteringExpressionsTree(filteringState.operator, filteringState.field); + newExpressionsTree = new FilteringExpressionsTree(filteringState.operator, filteringState.fieldName); newExpressionsTree.filteringOperands = [...filteringState.filteringOperands]; } if (condition) { - const newExpression: IFilteringExpression = { field: fieldName, searchVal, condition, conditionName: condition.name, ignoreCase }; + const newExpression: IFilteringExpression = { fieldName: fieldName, searchVal, condition, conditionName: condition.name, ignoreCase }; expressionsTree = new FilteringExpressionsTree(filteringState.operator, fieldName); expressionsTree.filteringOperands.push(newExpression); } diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 4296e6b2f88..38f0d292a6b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -1841,7 +1841,7 @@ export abstract class IgxGridBaseDirective implements GridType, const val = (value as FilteringExpressionsTree); for (let index = 0; index < val.filteringOperands.length; index++) { if (!(val.filteringOperands[index] instanceof FilteringExpressionsTree)) { - const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, val.filteringOperands[index].field); + const newExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, val.filteringOperands[index].fieldName); newExpressionsTree.filteringOperands.push(val.filteringOperands[index] as IFilteringExpression); val.filteringOperands[index] = newExpressionsTree; } diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index 7a1a857a628..ef5ce245191 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -797,15 +797,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -842,15 +845,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1200,7 +1206,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); @@ -1236,7 +1243,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), + conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1274,7 +1282,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), + conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1313,7 +1322,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 30; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), + conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1347,7 +1357,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.Or); for (let index = 0; index < 20; index++) { tree.filteringOperands.push({ - field: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals') + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), + conditionName: 'equals' }); } grid.advancedFilteringExpressionsTree = tree; @@ -1382,15 +1393,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1420,15 +1434,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1483,15 +1500,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1563,11 +1583,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1610,11 +1632,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1662,11 +1686,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1736,15 +1762,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -1786,11 +1815,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -1820,11 +1851,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -1857,24 +1890,29 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); const andTree = new FilteringExpressionsTree(FilteringLogic.Or); andTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); andTree.filteringOperands.push({ - field: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push(andTree); @@ -2074,15 +2112,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2120,15 +2161,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2184,15 +2228,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2248,11 +2295,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2305,15 +2354,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2362,15 +2414,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2406,15 +2461,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2462,15 +2520,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2528,15 +2589,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2606,7 +2670,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); @@ -2631,15 +2696,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); @@ -2707,11 +2775,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2744,11 +2814,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2778,11 +2850,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2812,11 +2886,13 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.Or); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; @@ -2849,15 +2925,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push(orTree); diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts index 3fab8cdeb6b..74f30858b5b 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-ui.spec.ts @@ -471,12 +471,14 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'g', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression1 = { fieldName: 'ProductName', searchVal: 'I', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -520,7 +522,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); @@ -592,12 +595,14 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { fieldName: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -756,7 +761,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'I', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); } @@ -822,13 +828,15 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression1 = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression2 = { fieldName: 'ProductName', searchVal: 'test', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression1); @@ -956,12 +964,14 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { fieldName: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -992,12 +1002,14 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { fieldName: 'ProductName', searchVal: 'for', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -1039,8 +1051,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1358,9 +1370,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1384,9 +1396,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'z', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1442,9 +1454,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1641,9 +1653,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1675,9 +1687,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1713,9 +1725,9 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1750,8 +1762,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -1841,10 +1853,10 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'e', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2333,11 +2345,11 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'x', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'y', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'x', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'y', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'i', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'g', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'n', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2409,8 +2421,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'o', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2497,8 +2509,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 25, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, - { field: 'Downloads', searchVal: 200, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { fieldName: 'Downloads', searchVal: 25, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThan' }, + { fieldName: 'Downloads', searchVal: 200, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), conditionName: 'lessThan' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -2621,7 +2633,8 @@ describe('IgxGrid - Filtering Row UI actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); grid.filteringExpressionsTree = filteringExpressionsTree; @@ -3331,8 +3344,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3351,7 +3364,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3385,7 +3398,7 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3419,8 +3432,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3448,8 +3461,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3474,8 +3487,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3507,8 +3520,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3563,8 +3576,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), conditionName: 'lessThan' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3607,8 +3620,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3626,8 +3639,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3654,8 +3667,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'ProductName'); columnsFilteringTree.filteringOperands = [ - { field: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains') }, - { field: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains') } + { fieldName: 'ProductName', searchVal: 'Angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' }, + { fieldName: 'ProductName', searchVal: 'Ignite', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -3753,8 +3766,8 @@ describe('IgxGrid - Filtering actions - Excel style filtering #grid', () => { const gridFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); const columnsFilteringTree = new FilteringExpressionsTree(FilteringLogic.Or, 'Downloads'); columnsFilteringTree.filteringOperands = [ - { field: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals') }, - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals') } + { fieldName: 'Downloads', searchVal: 254, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' }, + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' } ]; gridFilteringExpressionsTree.filteringOperands.push(columnsFilteringTree); grid.filteringExpressionsTree = gridFilteringExpressionsTree; @@ -6994,7 +7007,7 @@ const verifyGridSubmenuSize = (gridNativeElement: HTMLElement, expectedSize: Siz }; const verifyFilteringExpression = (operand: IFilteringExpression, fieldName: string, conditionName: string, searchVal: any) => { - expect(operand.field).toBe(fieldName); + expect(operand.fieldName).toBe(fieldName); expect(operand.condition.name).toBe(conditionName); expect(operand.searchVal).toEqual(searchVal); }; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts index 950416f1382..20e67a85b34 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering.spec.ts @@ -770,8 +770,8 @@ describe('IgxGrid - Filtering actions #grid', () => { const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); gridExpressionsTree.filteringOperands = [ - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, - { field: 'ID', searchVal: 4, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') } + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo'), conditionName: 'greaterThanOrEqualTo' }, + { fieldName: 'ID', searchVal: 4, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThan' } ]; grid.filteringExpressionsTree = gridExpressionsTree; @@ -839,14 +839,14 @@ describe('IgxGrid - Filtering actions #grid', () => { it('Should correctly apply two conditions to two columns at once.', fakeAsync(() => { const colDownloadsExprTree = new FilteringExpressionsTree(FilteringLogic.And, 'Downloads'); colDownloadsExprTree.filteringOperands = [ - { field: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo') }, - { field: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('lessThanOrEqualTo') } + { fieldName: 'Downloads', searchVal: 20, condition: IgxNumberFilteringOperand.instance().condition('greaterThanOrEqualTo'), conditionName: 'greaterThanOrEqualTo' }, + { fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('lessThanOrEqualTo'), conditionName: 'lessThanOrEqualTo' } ]; const colIdExprTree = new FilteringExpressionsTree(FilteringLogic.And, 'ID'); colIdExprTree.filteringOperands = [ - { field: 'ID', searchVal: 1, condition: IgxNumberFilteringOperand.instance().condition('greaterThan') }, - { field: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('lessThan') } + { fieldName: 'ID', searchVal: 1, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), conditionName: 'greaterThan' }, + { fieldName: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), conditionName: 'lessThan' } ]; const gridExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); @@ -870,12 +870,14 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression = { fieldName: 'Downloads', searchVal: 50, - condition: IgxNumberFilteringOperand.instance().condition('greaterThan') + condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }; const expression1 = { fieldName: 'Downloads', searchVal: 500, - condition: IgxNumberFilteringOperand.instance().condition('lessThan') + condition: IgxNumberFilteringOperand.instance().condition('lessThan'), + conditionName: 'lessThan' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -892,12 +894,14 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = { fieldName: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -916,12 +920,14 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression = { fieldName: 'ReleaseDate', searchVal: null, - condition: IgxDateFilteringOperand.instance().condition('yesterday') + condition: IgxDateFilteringOperand.instance().condition('yesterday'), + conditionName: 'yesterday' }; const expression1 = { fieldName: 'ReleaseDate', searchVal: today, - condition: IgxDateFilteringOperand.instance().condition('after') + condition: IgxDateFilteringOperand.instance().condition('after'), + conditionName: 'after' }; filteringExpressionsTree.filteringOperands.push(expression); filteringExpressionsTree.filteringOperands.push(expression1); @@ -939,7 +945,8 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression = { fieldName: 'ReleaseDate', searchVal: null, - condition: IgxDateFilteringOperand.instance().condition('yesterday') + condition: IgxDateFilteringOperand.instance().condition('yesterday'), + conditionName: 'yesterday' }; filteringExpressionsTree.filteringOperands.push(expression); gridExpressionsTree.filteringOperands.push(filteringExpressionsTree); @@ -974,29 +981,34 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Ignite', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; const expression1 = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression11 = { fieldName: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression12 = { fieldName: 'ProductName', searchVal: 'jQuery', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression2 = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression21 = { fieldName: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; const expression22 = { fieldName: 'ProductName', searchVal: 'jQuery', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; expression1.filteringOperands.push(expression11); expression1.filteringOperands.push(expression12); @@ -1063,7 +1075,8 @@ describe('IgxGrid - Filtering actions #grid', () => { const expression11 = { fieldName: 'ProductName', searchVal: 'Angular', - condition: IgxStringFilteringOperand.instance().condition('contains') + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains' }; // Verify results after filtering. diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts index 44be1015ce8..40d913e1ccb 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-row-selection.spec.ts @@ -2013,14 +2013,16 @@ describe('IgxGrid - Row Selection #grid', () => { grid.height = '1100px'; const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'UnitsInStock', + fieldName: 'UnitsInStock', searchVal: 0, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); tree.filteringOperands.push({ - field: 'ProductName', + fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); grid.advancedFilteringExpressionsTree = tree; diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts index 21109a6b21e..1f0fc13f472 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid.component.spec.ts @@ -563,7 +563,8 @@ describe('IgxGrid Component Tests #grid', () => { grid.filteringExpressionsTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('equals'), - field: 'index', + conditionName: 'equals', + fieldName: 'index', searchVal: 0 } ]; diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts index f97dff86967..d6746fbf431 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts @@ -109,7 +109,8 @@ describe('IgxHierarchicalGrid Virtualization #hGrid', () => { const expression = { fieldName: 'ProductName', searchVal: 'Product: A0', - condition: IgxStringFilteringOperand.instance().condition('startsWith') + condition: IgxStringFilteringOperand.instance().condition('startsWith'), + conditionName: 'startsWith' }; filteringExpressionsTree.filteringOperands.push(expression); childGrid.filter('ProductName', null, filteringExpressionsTree); diff --git a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts index 273b5d1e2e5..93c4599b10c 100644 --- a/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/pivot-grid/pivot-grid.spec.ts @@ -227,7 +227,8 @@ describe('IgxPivotGrid #pivotGrid', () => { filteringExpressionTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('equals'), - field: 'SellerName', + conditionName: 'equals', + fieldName: 'SellerName', searchVal: 'Stanley' } ]; @@ -2731,7 +2732,8 @@ describe('IgxPivotGrid #pivotGrid', () => { filterColumnExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('in'), - field: 'City', + conditionName: 'in', + fieldName: 'City', searchVal: new Set(['Ciudad de la Costa']) } ]; @@ -2739,7 +2741,8 @@ describe('IgxPivotGrid #pivotGrid', () => { filterRowExpTree.filteringOperands = [ { condition: IgxStringFilteringOperand.instance().condition('in'), - field: 'ProductCategory', + conditionName: 'in', + fieldName: 'ProductCategory', searchVal: new Set(['Bikes']) } ]; diff --git a/projects/igniteui-angular/src/lib/grids/state-base.directive.ts b/projects/igniteui-angular/src/lib/grids/state-base.directive.ts index 4c15d29306a..09b47274c12 100644 --- a/projects/igniteui-angular/src/lib/grids/state-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/state-base.directive.ts @@ -642,7 +642,7 @@ export class IgxGridStateBaseDirective { return null; } - const expressionsTree = new FilteringExpressionsTree(exprTreeObject.operator, exprTreeObject.field); + const expressionsTree = new FilteringExpressionsTree(exprTreeObject.operator, exprTreeObject.fieldName); for (const item of exprTreeObject.filteringOperands) { // Check if item is an expressions tree or a single expression. @@ -653,11 +653,11 @@ export class IgxGridStateBaseDirective { const expr = item as IFilteringExpression; let dataType: string; if (this.currGrid instanceof IgxPivotGridComponent) { - dataType = this.currGrid.allDimensions.find(x => x.memberName === expr.field).dataType; + dataType = this.currGrid.allDimensions.find(x => x.memberName === expr.fieldName).dataType; } else if (this.currGrid.columns.length > 0) { - dataType = this.currGrid.columns.find(c => c.field === expr.field).dataType; + dataType = this.currGrid.columns.find(c => c.field === expr.fieldName).dataType; } else if (this.state.columns) { - dataType = this.state.columns.find(c => c.field === expr.field).dataType; + dataType = this.state.columns.find(c => c.field === expr.fieldName).dataType; } else { return null; } @@ -671,7 +671,7 @@ export class IgxGridStateBaseDirective { } const condition = this.generateFilteringCondition(dataType, expr.condition.name) || - this.currGrid.columns.find(c => c.field === expr.field).filters.condition(expr.condition.name); + this.currGrid.columns.find(c => c.field === expr.fieldName).filters.condition(expr.condition.name); if (condition) { expr.condition = condition; diff --git a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts index 292fb7741c0..013ca246a1d 100644 --- a/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.directive.spec.ts @@ -103,6 +103,7 @@ describe('IgxGridState - input properties #grid', () => { const productFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const productExpression = { condition: IgxBooleanFilteringOperand.instance().condition('true'), + conditionName: 'true', fieldName: 'InStock', ignoreCase: true }; @@ -749,7 +750,7 @@ class HelperFunctions { } public static verifyFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { - expect(expressions.field).toBe(gridState.filtering.field, 'Filtering expression field name is not correct'); + expect(expressions.fieldName).toBe(gridState.filtering.fieldName, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.filtering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.filtering.filteringOperands[i])); @@ -758,7 +759,7 @@ class HelperFunctions { public static verifyAdvancedFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { if (gridState.advancedFiltering) { - expect(expressions.field).toBe(gridState.advancedFiltering.field, 'Filtering expression field name is not correct'); + expect(expressions.fieldName).toBe(gridState.advancedFiltering.fieldName, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.advancedFiltering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.advancedFiltering.filteringOperands[i])); diff --git a/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts index 5b20391b20e..92cc08c8f86 100644 --- a/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.hierarchicalgrid.spec.ts @@ -109,6 +109,7 @@ describe('IgxHierarchicalGridState - input properties #hGrid', () => { const productFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const productExpression = { condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', fieldName: 'ProductName', ignoreCase: true, searchVal: 'A0' @@ -516,7 +517,7 @@ class HelperFunctions { } public static verifyFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { - expect(expressions.field).toBe(gridState.filtering.field, 'Filtering expression field name is not correct'); + expect(expressions.fieldName).toBe(gridState.filtering.fieldName, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.filtering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.filtering.filteringOperands[i])); @@ -525,7 +526,7 @@ class HelperFunctions { public static verifyAdvancedFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { if (gridState.advancedFiltering) { - expect(expressions.field).toBe(gridState.advancedFiltering.field, 'Filtering expression field name is not correct'); + expect(expressions.fieldName).toBe(gridState.advancedFiltering.fieldName, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.advancedFiltering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.advancedFiltering.filteringOperands[i])); diff --git a/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts b/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts index e554c8edf07..67cbc9ff47e 100644 --- a/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/state.treegrid.spec.ts @@ -101,6 +101,7 @@ describe('IgxTreeGridState - input properties #tGrid', () => { const productFilteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'Age'); const productExpression = { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', fieldName: 'Age', ignoreCase: true, searchVal: 35 @@ -307,7 +308,7 @@ class HelperFunctions { } public static verifyFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { - expect(expressions.field).toBe(gridState.filtering.field, 'Filtering expression field name is not correct'); + expect(expressions.fieldName).toBe(gridState.filtering.fieldName, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.filtering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.filtering.filteringOperands[i])); @@ -316,7 +317,7 @@ class HelperFunctions { public static verifyAdvancedFilteringExpressions(expressions: IFilteringExpressionsTree, gridState: IGridState) { if (gridState.advancedFiltering) { - expect(expressions.field).toBe(gridState.advancedFiltering.field, 'Filtering expression field name is not correct'); + expect(expressions.fieldName).toBe(gridState.advancedFiltering.fieldName, 'Filtering expression field name is not correct'); expect(expressions.operator).toBe(gridState.advancedFiltering.operator, 'Filtering expression operator value is not correct'); expressions.filteringOperands.forEach((expr, i) => { expect(expr).toEqual(jasmine.objectContaining(gridState.advancedFiltering.filteringOperands[i])); diff --git a/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts b/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts index fb688a045d6..fa3dad5d6ee 100644 --- a/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts +++ b/projects/igniteui-angular/src/lib/grids/summaries/grid-summary.service.ts @@ -165,7 +165,7 @@ export class IgxGridSummaryService { private deleteSummaryCache(id, columnName) { if (this.summaryCacheMap.get(id)) { const filteringApplied = columnName && this.grid.filteringExpressionsTree && - this.grid.filteringExpressionsTree.filteringOperands.map((expr) => expr.field).indexOf(columnName) !== -1; + this.grid.filteringExpressionsTree.filteringOperands.map((expr) => expr.fieldName).indexOf(columnName) !== -1; if (columnName && this.summaryCacheMap.get(id).get(columnName) && !filteringApplied) { this.summaryCacheMap.get(id).delete(columnName); } else { diff --git a/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts b/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts index 201f16476c8..415a83ffb56 100644 --- a/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts +++ b/projects/igniteui-angular/src/lib/grids/toolbar/grid-toolbar-advanced-filtering.component.ts @@ -66,7 +66,7 @@ export class IgxGridToolbarAdvancedFilteringComponent implements AfterViewInit { if (expr instanceof FilteringExpressionsTree) { columnNames.push(...this.extractUniqueFieldNamesFromFilterTree(expr)); } else { - columnNames.push((expr as IFilteringExpression).field); + columnNames.push((expr as IFilteringExpression).fieldName); } }); return [...new Set(columnNames)]; diff --git a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts index 7b9cb92bcea..f6d5bc3ccd3 100644 --- a/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid-selection.spec.ts @@ -1398,17 +1398,20 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 299 } ]; @@ -1448,17 +1451,20 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 299 } ]; @@ -1500,7 +1506,8 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 957 }, ]; @@ -1544,17 +1551,20 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 998 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 299 } ]; @@ -1615,12 +1625,14 @@ describe('IgxTreeGrid - Selection #tGrid', () => { expressionTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 711 }, { condition: IgxNumberFilteringOperand.instance().condition('doesNotEqual'), - field: 'ID', + conditionName: 'doesNotEqual', + fieldName: 'ID', searchVal: 998 } ]; diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 5c0d1556461..7c8717fe481 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -565,7 +565,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.cancelOperandAdd(); const operandItem = new ExpressionOperandItem({ - field: null, + fieldName: null, condition: null, conditionName: null, ignoreCase: true, @@ -608,7 +608,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public commitOperandEdit() { if (this._editedExpression) { - this._editedExpression.expression.field = this.selectedField.field; + this._editedExpression.expression.fieldName = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); this._editedExpression.expression.searchVal = DataUtil.parseValue(this.selectedField.dataType, this.searchValue); this._editedExpression.fieldLabel = this.selectedField.label @@ -664,7 +664,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (this._editedExpression) { this._editedExpression.inEditMode = false; - if (!this._editedExpression.expression.field) { + if (!this._editedExpression.expression.fieldName) { this.deleteItem(this._editedExpression); } @@ -757,8 +757,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { expressionItem.hovered = false; this.fields = this.selectedEntity ? this.selectedEntity.fields : null; this.selectedField = - expressionItem.expression.field ? - this.fields.find(field => field.field === expressionItem.expression.field) + expressionItem.expression.fieldName ? + this.fields.find(field => field.field === expressionItem.expression.fieldName) : null; this.selectedCondition = expressionItem.expression.condition ? @@ -1163,7 +1163,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } else { const filteringExpr = expr as IFilteringExpression; const exprCopy: IFilteringExpression = { - field: filteringExpr.field, + fieldName: filteringExpr.fieldName, condition: filteringExpr.condition, conditionName: filteringExpr.condition.name, searchVal: filteringExpr.searchVal, @@ -1175,7 +1175,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (entity) { this.fields = entity.fields; } - const field = this.fields.find(el => el.field === filteringExpr.field); + const field = this.fields.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field.label || field.header || field.field; groupItem.children.push(operandItem); this._selectedEntity = this.entities.find(el => el.name === expressionTree.entity); diff --git a/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts index 50bd4b50f46..c497e830729 100644 --- a/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/services/csv/csv-exporter-grid.spec.ts @@ -341,21 +341,24 @@ describe('CSV Grid Exporter', () => { const grid = fix.componentInstance.grid; const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ID', + fieldName: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; @@ -492,15 +495,17 @@ describe('CSV Grid Exporter', () => { it('Should honor the Advanced filters when exporting', async () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); treeGrid.advancedFilteringExpressionsTree = tree; diff --git a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts index bd936ff899d..8326bad6d46 100644 --- a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts +++ b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts @@ -589,21 +589,24 @@ describe('Excel Exporter', () => { const grid = fix.componentInstance.grid; const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'r', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); tree.filteringOperands.push({ - field: 'ID', + fieldName: 'ID', searchVal: 5, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' }); grid.advancedFilteringExpressionsTree = tree; @@ -1144,15 +1147,17 @@ describe('Excel Exporter', () => { it('Should honor Advanced filters when exporting', async () => { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'Age', + fieldName: 'Age', searchVal: 40, condition: IgxNumberFilteringOperand.instance().condition('lessThan'), + conditionName: 'lessThan', ignoreCase: true }); tree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', ignoreCase: true }); diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts index be58b8d2a68..cc89c04db5d 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts @@ -2070,7 +2070,8 @@ export class IgxGridFilteringBindingComponent extends BasicGridComponent impleme this.filterTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - field: 'Downloads', + conditionName: 'greaterThan', + fieldName: 'Downloads', searchVal: 200 } ]; @@ -2101,7 +2102,8 @@ export class IgxGridAdvancedFilteringBindingComponent extends BasicGridComponent this.filterTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - field: 'Downloads', + conditionName: 'greaterThan', + fieldName: 'Downloads', searchVal: 200 } ]; diff --git a/src/app/grid-external-filtering/grid-external-filtering.sample.ts b/src/app/grid-external-filtering/grid-external-filtering.sample.ts index 6261bb0815d..ab05796ef10 100644 --- a/src/app/grid-external-filtering/grid-external-filtering.sample.ts +++ b/src/app/grid-external-filtering/grid-external-filtering.sample.ts @@ -36,7 +36,7 @@ export class GridExternalFilteringComponent implements OnInit, AfterViewInit { public ngAfterViewInit(): void { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'ID', + fieldName: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'a', @@ -44,14 +44,14 @@ export class GridExternalFilteringComponent implements OnInit, AfterViewInit { }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ID', + fieldName: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'b', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'CompanyName', + fieldName: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'c', @@ -59,7 +59,7 @@ export class GridExternalFilteringComponent implements OnInit, AfterViewInit { }); tree.filteringOperands.push(orTree); tree.filteringOperands.push({ - field: 'CompanyName', + fieldName: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'd', diff --git a/src/app/grid-filtering/grid-filtering.sample.ts b/src/app/grid-filtering/grid-filtering.sample.ts index 85be6257a97..c1ed0fbd22f 100644 --- a/src/app/grid-filtering/grid-filtering.sample.ts +++ b/src/app/grid-filtering/grid-filtering.sample.ts @@ -75,7 +75,7 @@ export class GridFilteringComponent implements OnInit { const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ - field: 'ID', + fieldName: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'a', @@ -83,14 +83,14 @@ export class GridFilteringComponent implements OnInit { }); const orTree = new FilteringExpressionsTree(FilteringLogic.Or); orTree.filteringOperands.push({ - field: 'ID', + fieldName: 'ID', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'b', ignoreCase: true }); orTree.filteringOperands.push({ - field: 'CompanyName', + fieldName: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'c', @@ -98,7 +98,7 @@ export class GridFilteringComponent implements OnInit { }); tree.filteringOperands.push(orTree); tree.filteringOperands.push({ - field: 'CompanyName', + fieldName: 'CompanyName', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', searchVal: 'd', diff --git a/src/app/pivot-grid/pivot-grid.sample.ts b/src/app/pivot-grid/pivot-grid.sample.ts index 21f4f0df131..063699d3d43 100644 --- a/src/app/pivot-grid/pivot-grid.sample.ts +++ b/src/app/pivot-grid/pivot-grid.sample.ts @@ -73,7 +73,7 @@ export class PivotGridSampleComponent { { condition: IgxStringFilteringOperand.instance().condition('equals'), conditionName: IgxStringFilteringOperand.instance().condition('equals').name, - field: 'SellerName', + fieldName: 'SellerName', searchVal: 'Stanley' } ]; diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 477c8dea10b..6a027f4d428 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -46,39 +46,39 @@ export class QueryBuilderComponent implements OnInit { } ]; - const innerTree = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', ['Id']); + const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Assays', ['Id']); innerTree.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', condition: IgxStringFilteringOperand.instance().condition('equals'), conditionName: IgxStringFilteringOperand.instance().condition('equals').name, searchVal: 'Hepacity', // ignoreCase: true }); innerTree.filteringOperands.push({ - field: 'EndpointName', + fieldName: 'EndpointName', condition: IgxStringFilteringOperand.instance().condition('equals'), conditionName: IgxStringFilteringOperand.instance().condition('equals').name, searchVal: 'IC60', // ignoreCase: true }); - const innerTree2 = new FilteringExpressionsTree(FilteringLogic.And, 'Assays', ['Name']); + const innerTree2 = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Assays', ['Name']); innerTree2.filteringOperands.push({ - field: 'Name', + fieldName: 'Name', condition: IgxStringFilteringOperand.instance().condition('null'), conditionName: IgxStringFilteringOperand.instance().condition('null').name, // ignoreCase: true }); - const tree = new FilteringExpressionsTree(FilteringLogic.And, 'Compounds', ['*']); + const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Compounds', ['*']); tree.filteringOperands.push({ - field: 'Id', + fieldName: 'Id', condition: IgxStringFilteringOperand.instance().condition('in'), conditionName: IgxStringFilteringOperand.instance().condition('in').name, searchTree: innerTree }); tree.filteringOperands.push({ - field: 'Id', + fieldName: 'Id', condition: IgxStringFilteringOperand.instance().condition('equals'), conditionName: IgxStringFilteringOperand.instance().condition('equals').name, searchVal: '123', From 3616048880c2cc7d16b6ee31de6c054db7b42969 Mon Sep 17 00:00:00 2001 From: teodosia Date: Thu, 19 Sep 2024 11:11:46 +0300 Subject: [PATCH 064/147] feat(query-builder): change theming version in projects package.json --- projects/igniteui-angular/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/package.json b/projects/igniteui-angular/package.json index d461f1fab3c..24dd51918e6 100644 --- a/projects/igniteui-angular/package.json +++ b/projects/igniteui-angular/package.json @@ -74,7 +74,7 @@ "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "uuid": "^9.0.0", - "igniteui-theming": "^11.0.0", + "igniteui-theming": "^11.0.1-beta.1", "@igniteui/material-icons-extended": "^3.0.0" }, "peerDependencies": { From a74e7727ff845d0539789cae6c49fe7cee4a53a2 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 19 Sep 2024 11:51:06 +0300 Subject: [PATCH 065/147] fix(query-builder): Post-merge fixups --- .../hierarchical-grid.virtualization.spec.ts | 2 +- .../src/lib/query-builder/query-builder-tree.component.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts index ce3952abfb8..d6746fbf431 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.virtualization.spec.ts @@ -107,7 +107,7 @@ describe('IgxHierarchicalGrid Virtualization #hGrid', () => { const filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And, 'ProductName'); const expression = { - field: 'ProductName', + fieldName: 'ProductName', searchVal: 'Product: A0', condition: IgxStringFilteringOperand.instance().condition('startsWith'), conditionName: 'startsWith' diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 49a81a5d231..028e6d3f7c6 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -744,7 +744,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._editedExpression.expression.searchTree.returnFields = innerQuery.selectedReturnFields; this._editedExpression.expression.searchTree.filteringOperands = innerQuery.expressionTree.filteringOperands; this._editedExpression.expression.searchTree.operator = innerQuery.expressionTree.operator; - this._editedExpression.expression.searchTree.field = innerQuery.expressionTree.field; + this._editedExpression.expression.searchTree.fieldName = innerQuery.expressionTree.fieldName; } else { this._editedExpression.expression.searchTree = null; } @@ -1541,7 +1541,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } - + private init() { this.clearSelection(); this.cancelOperandAdd(); From 8460a5cbadce4ff90e052a7d0eb786638b82f40b Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 19 Sep 2024 12:56:04 +0300 Subject: [PATCH 066/147] chore(query-builder): Marked find, findIndex as deprecated --- .../src/lib/data-operations/filtering-expressions-tree.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts index 09b7cee7bf3..12642c080c0 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts @@ -21,7 +21,14 @@ export declare interface IFilteringExpressionsTree extends IBaseEventArgs, IExpr /* alternateName: treeType */ type?: FilteringExpressionsTreeType; + /** + * @deprecated + */ find(fieldName: string): IFilteringExpressionsTree | IFilteringExpression; + + /** + * @deprecated + */ findIndex(fieldName: string): number; } From 69170b180b9d0fbe061c55d8cf7cd78c609f6eb3 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 19 Sep 2024 14:10:41 +0300 Subject: [PATCH 067/147] fix(query-builder): Fixed fieldName in templates post rename --- .../lib/query-builder/query-builder-tree.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 416d50b9fc4..964f8060e70 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -91,7 +91,7 @@
(selectedChanged)="onChipSelectionEnd()" > - {{expressionItem.fieldLabel || expressionItem.expression.field}} + {{expressionItem.fieldLabel || expressionItem.expression.fieldName}} {{ @@ -111,17 +111,17 @@
{{ isDate(expressionItem.expression.searchVal) - ? getFormatter(expressionItem.expression.field) + ? getFormatter(expressionItem.expression.fieldName) ? (expressionItem.expression.searchVal | fieldFormatter : getFormatter( - expressionItem.expression.field + expressionItem.expression.fieldName ) : undefined) : (expressionItem.expression.searchVal | date : getFormat( - expressionItem.expression.field + expressionItem.expression.fieldName ) : undefined : this.locale) From 7d58abfc74781fb2d0eaebcdd9cfffc6e14102c5 Mon Sep 17 00:00:00 2001 From: Teodosia Hristodorova <52423497+teodosiah@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:06:51 +0300 Subject: [PATCH 068/147] Add Query Builder tests (#14671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(query-builder): add appearance tests * test(query-builder): add custom header test * test(query-builder): add adding new group tests & inputs validation functions * test(query-builder): fix inputs validation and add inputs state tests * test(query-builder): add Keyboard navigation tests for chip and group line * test(query-builder): fix local function hitKeyUponElementAndDetectChanges * test(query-builder): add fields,columns,operator type validation tests,fix lint * test(query-builder): fix unused variable value lint error * test(query-builder): fix failing tests * test(query-builder): remove fdescribe * test(query-builder): add condition data types tests * test(query-builder): add change entity dialog tests * test(query-builder): add tab navigation test * test(query-builder): apply in condition through UI test * test(query-builder): fix Improved tab test * test(query-builder): add expand/collapse button tests * test(query-builder): add tabbable elements tests. Code cleanup * test(query-builder): fix altered chip selection verification * test(query-builder): add new group/condition tests * test(query-builder): add new interaction test * test(query-builder): edit constant strings are now in a separate class * test(query-builder): chip selection tests * test(query-builder): add open/close edit mode test * test(query-builder): add dbclick & operator click tests * test(query-builder): add expressionTreeChange emit tests * test(query-builder): edit reverting condition fileds test * test(query-builder): add group context menu show/hide test * test(query-builder): add context menu open/close from line and close btn test * test(query-builder): fix lint and formatting * test(query-builder): add new group/ungroup conditions * test(query-builder): fix Test name unification with legacy ones * test(query-builder): add chip close button Enter test * test(query-builder): add new condition count function * test(query-builder): adding buttons state validation test * test(query-builder): click on parent commit button test * test(query-builder): add group removal test * test(query-builder): еnd group button tests * test(query-builder): in/not-in conditions tests * test(query-builder): localization test and refactoring * test(query-builder): removed IgxQueryBuiderExprTreeSampleTestComponent * test(query-builder): fix spelling errors * test(query-builder): add pending test * test(query-builder): refactor tests, names and functions * test(query-builder): add custom input template test * feat(query-builder): remove QB related tests from general AF tests * test(query-builder): revert bad merge push * fix(query-builder): Revert e32430e * test(query-builder): add common template semple component * test(query-builder): refactor AF General tests * test(query-builder): refactor AF Column Groups tests * test(query-builder): refactor AF External tests * test(query-builder): refactor AF Localization tests & grid AF functions * test(query-builder): remove fit in AF spec * test(query-builder): fix two failing tests * test(query-builder): fix fieldName and contructor change * test(query-builder): fix remove fdescribe --------- Co-authored-by: INFRAGISTICS\IPetrov Co-authored-by: Galina Edinakova --- .../grid/grid-filtering-advanced.spec.ts | 3134 +++-------------- .../query-builder/query-builder-functions.ts | 903 +++++ .../query-builder.component.spec.ts | 2121 +++++++++++ .../src/lib/test-utils/grid-functions.spec.ts | 404 +-- 4 files changed, 3525 insertions(+), 3037 deletions(-) create mode 100644 projects/igniteui-angular/src/lib/query-builder/query-builder-functions.ts create mode 100644 projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index 295e7199dd4..6c4f1595466 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -1,35 +1,29 @@ import { fakeAsync, TestBed, tick, flush, ComponentFixture } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxGridComponent } from './grid.component'; -import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; +import { UIInteractions } from '../../test-utils/ui-interactions.spec'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { IgxNumberFilteringOperand, IgxStringFilteringOperand } from '../../data-operations/filtering-condition'; import { GridFunctions } from '../../test-utils/grid-functions.spec'; -import { FilteringExpressionsTree, IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; +import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { FilteringLogic } from '../../data-operations/filtering-expression.interface'; import { IgxGridAdvancedFilteringColumnGroupComponent, IgxGridAdvancedFilteringComponent, IgxGridExternalAdvancedFilteringComponent, IgxGridAdvancedFilteringBindingComponent, - IgxGridAdvancedFilteringOverlaySettingsComponent, IgxGridAdvancedFilteringDynamicColumnsComponent } from '../../test-utils/grid-samples.spec'; -import { ControlsFunction } from '../../test-utils/controls-functions.spec'; import { FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy'; import { IgxHierGridExternalAdvancedFilteringComponent } from '../../test-utils/hierarchical-grid-components.spec'; import { IgxHierarchicalGridComponent } from '../hierarchical-grid/public_api'; import { IFilteringEventArgs } from '../public_api'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; - -const ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS = 'igx-filter-tree__line--and'; -const ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS = 'igx-filter-tree__line--or'; -const ADVANCED_FILTERING_OPERATOR_LINE_SELECTED_CSS_CLASS = 'igx-filter-tree__line--selected'; -const ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS = 'igx-filter-tree__expression-item'; -const CHIP_SELECT_CLASS = '.igx-chip__select'; +import { QueryBuilderConstants, QueryBuilderFunctions } from '../../query-builder/query-builder-functions'; +import { By } from '@angular/platform-browser'; describe('IgxGrid - Advanced Filtering #grid - ', () => { configureTestSuite((() => { @@ -81,13 +75,16 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); + const advFilteringDialog = GridFunctions.getAdvancedFilteringComponent(fix); + // Verify AF dialog is opened. - expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull(); + expect(advFilteringDialog).not.toBeNull(); + expect(advFilteringDialog.querySelector('igx-query-builder')).not.toBeNull(); // Verify there are not filters present and that the default text is shown. expect(grid.advancedFilteringExpressionsTree).toBeUndefined(); - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull(); - expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).not.toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix)).toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix)).not.toBeNull(); // Close Advanced Filtering dialog. GridFunctions.clickAdvancedFilteringCancelButton(fix); @@ -119,19 +116,19 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. + let input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Close dialog through API. @@ -151,14 +148,14 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { fix.detectChanges(); // Double-click the existing chip to enter edit mode. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0], true); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true); tick(50); fix.detectChanges(); // Edit the filter value. - input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'some non-existing value', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Close dialog through API. @@ -174,143 +171,7 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular'); })); - it('Should show/hide initial adding buttons depending on the existence of groups.', fakeAsync(() => { - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); - - // Verify that the initial buttons are visible. - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(2); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Verify that the initial buttons are not visible. - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(0); - - // Discard the new group and verify that the initial buttons are visible. - GridFunctions.clickAdvancedFilteringExpressionCloseButton(fix); - fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(2); - - // Click the initial 'Add Or Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); - - // Verify that the initial buttons are not visible. - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(0); - })); - - it('Should correctly initialize a newly added \'And\' group.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Verify there is a new root group, which is empty. - const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(group).not.toBeNull('There is no root group.'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group).length).toBe(0, 'The group has children.'); - - // Verify the operator line of the root group is an 'And' line. - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix), 'and'); - - // Verify the enabled/disabled state of each input of the expression in edit mode. - verifyEditModeExpressionInputStates(fix, true, false, false, false); - - // Verify the edit inputs are empty. - verifyEditModeExpressionInputValues(fix, '', '', ''); - - // Verify adding buttons are disabled. - const buttons = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0); - for (const button of buttons) { - ControlsFunction.verifyButtonIsDisabled(button); - } - })); - - it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); - - // Verify there is a new root group, which is empty. - const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(group).not.toBeNull('There is no root group.'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group).length).toBe(0, 'The group has children.'); - - // Verify the operator line of the root group is an 'Or' line. - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix), 'or'); - - // Verify the enabled/disabled state of each input of the expression in edit mode. - verifyEditModeExpressionInputStates(fix, true, false, false, false); - - // Verify the edit inputs are empty. - verifyEditModeExpressionInputValues(fix, '', '', ''); - - // Verify adding buttons are disabled. - const buttons = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0); - for (const button of buttons) { - ControlsFunction.verifyButtonIsDisabled(button); - } - })); - - it('Should add a new group through initial adding button and filter by it.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Verify the enabled/disabled state of each input of the expression in edit mode. - verifyEditModeExpressionInputStates(fix, true, false, false, false); - - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - verifyEditModeExpressionInputStates(fix, true, true, false, false); - - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - verifyEditModeExpressionInputStates(fix, true, true, true, false); - - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. - tick(); - fix.detectChanges(); - verifyEditModeExpressionInputStates(fix, true, true, true, true); - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Verify the new expression has been added to the group. - const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildExpressions(group).length).toBe(1, 'Incorrect children count.'); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); - fix.detectChanges(); - - // Verify the filter results. - expect(grid.filteredData.length).toEqual(2); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular'); - })); - - it('Should update the Advanced Filtering button in toolbar when (filtering)/(clear filtering).', - fakeAsync(() => { + it('Should update the Advanced Filtering button in toolbar when (filtering)/(clear filtering).', fakeAsync(() => { // Verify that the advanced filtering button indicates there are no filters. let advFilterBtn = GridFunctions.getAdvancedFilteringButton(fix); expect(Array.from(advFilterBtn.children).some(c => (c as any).classList.contains('igx-adv-filter--column-number'))) @@ -320,18 +181,19 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value. + // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Apply the filters. @@ -379,404 +241,264 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { expect(applyButtonType).toBe(expectedButtonType, 'Apply button type is not "button"'); }); - it('Should correctly display header name in select dropdown and in chip expression.', fakeAsync(() => { + it('Should emit the filtering event when applying filters.', fakeAsync(() => { + spyOn(grid.filtering, 'emit'); + // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - // Open column dropdown and verify header name is displayed for first item - GridFunctions.clickAdvancedFilteringColumnSelect(fix); - fix.detectChanges(); - const dropdownItems = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix); - expect(dropdownItems[0].innerText).toBe('HeaderID'); - - selectColumnInEditModeExpression(fix, 0); // Select 'HeaderID' column - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value. + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); - // Verify header name in chip text - verifyExpressionChipContent(fix, [0], 'HeaderID', 'Contains', 'a'); - // Apply the filters. GridFunctions.clickAdvancedFilteringApplyButton(fix); - fix.detectChanges(); - - // Close Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringCancelButton(fix); tick(100); fix.detectChanges(); - // Open Advanced Filtering dialog again. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify header name in chip text - verifyExpressionChipContent(fix, [0], 'HeaderID', 'Contains', 'a'); + // Ensure that filtering event was emitted with expected arguments + expect(grid.filtering.emit).toHaveBeenCalledWith(jasmine.objectContaining({ + owner: grid, + filteringExpressions: grid.advancedFilteringExpressionsTree, + cancel: false + })); })); - it('Should correctly filter by a \'string\' column through UI.', fakeAsync(() => { - // Test prerequisites - grid.height = '800px'; - fix.detectChanges(); - tick(50); - - // Verify no filters are present. - expect(grid.filteredData).toBeNull(); - expect(grid.rowList.length).toBe(8); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); + it('Should cancel filtering if cancel is set to true.', fakeAsync(() => { + spyOn(grid.filtering, 'emit').and.callFake((args: IFilteringEventArgs) => { + args.cancel = true; + }); // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Apply the filters. GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); fix.detectChanges(); - // Verify the filter results. - expect(grid.filteredData.length).toEqual(2); - expect(grid.rowList.length).toBe(2); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular'); - })); + // Ensure that cancel flag is true + expect(grid.filtering.emit).toHaveBeenCalled(); + const emittedArgs: IFilteringEventArgs = (grid.filtering.emit as jasmine.Spy).calls.mostRecent().args[0]; + expect(emittedArgs.cancel).toBeTrue(); - it('Should correctly filter by a \'Greater Than\' with \'number\' column through UI.', fakeAsync(() => { - // Test prerequisites - grid.height = '800px'; - fix.detectChanges(); - tick(50); + // Ensure that grid.filteredData is null + expect(grid.filteredData).toEqual(null); + })); - // Verify no filters are present. - expect(grid.filteredData).toBeNull(); - expect(grid.rowList.length).toBe(8); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); + it('Should emit the filteringDone event when applying filters.', fakeAsync(() => { + spyOn(grid.filteringDone, 'emit'); // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, '20', fix); // Type filter value. + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Apply the filters. GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); fix.detectChanges(); - // Verify the filter results. - expect(grid.filteredData.length).toEqual(5); - expect(grid.rowList.length).toBe(5); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); + expect(grid.filteringDone.emit).toHaveBeenCalledWith(grid.advancedFilteringExpressionsTree); + expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number').textContent).toContain('1'); })); - it('Should correctly filter by a \'Equals\' with \'number\' column through UI.', fakeAsync(() => { - // Test prerequisites + it('Applying/Clearing filter through the API should correctly update the UI and correctly show number of filtered columns', fakeAsync(() => { grid.height = '800px'; fix.detectChanges(); tick(50); - // Verify no filters are present. + // Verify the initial state of the grid and that no filters are present. expect(grid.filteredData).toBeNull(); - expect(grid.rowList.length).toBe(8); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); + expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number')).toBeNull(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); + // Apply advanced filter through API. + const tree = new FilteringExpressionsTree(FilteringLogic.And); + tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' + }); + const orTree = new FilteringExpressionsTree(FilteringLogic.Or); + orTree.filteringOperands.push({ + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', + ignoreCase: true + }); + orTree.filteringOperands.push({ + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', + ignoreCase: true + }); + tree.filteringOperands.push(orTree); + grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); - selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, '127', fix); // Type filter value. - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); + // Verify the state of the grid after filtering. + expect(grid.filteredData.length).toBe(2); + expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number').textContent).toContain('(2)'); + expect(GridFunctions.getExcelFilterIconFiltered(fix, 'ProductName')).toBeDefined(); + expect(GridFunctions.getExcelFilterIconFiltered(fix, 'Downloads')).toBeDefined(); - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); + // Clear filters through API. + grid.advancedFilteringExpressionsTree = null; fix.detectChanges(); - // Verify the filter results. - expect(grid.filteredData.length).toEqual(1); - expect(grid.rowList.length).toBe(1); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('NetAdvantage'); + // Verify there are not filters present and that the default text is shown. + expect(grid.advancedFilteringExpressionsTree).toBeNull(); + expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number')).toBeNull(); })); - it('Should correctly filter by a \'boolean\' column through UI.', fakeAsync(() => { + it('Applying/Clearing filter through the API should correctly update the UI.', fakeAsync(() => { // Test prerequisites grid.height = '800px'; fix.detectChanges(); tick(50); - // Verify no filters are present. + // Verify the initial state of the grid and that no filters are present. expect(grid.filteredData).toBeNull(); expect(grid.rowList.length).toBe(8); expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); + // Apply advanced filter through API. + const tree = new FilteringExpressionsTree(FilteringLogic.And); + tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' + }); + const orTree = new FilteringExpressionsTree(FilteringLogic.Or); + orTree.filteringOperands.push({ + fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', + ignoreCase: true + }); + orTree.filteringOperands.push({ + fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', + ignoreCase: true + }); + tree.filteringOperands.push(orTree); + grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); + // Verify the state of the grid after filtering. + expect(grid.filteredData.length).toBe(2); + expect(grid.rowList.length).toBe(2); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); + + // Open Advanced Filtering dialog. + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - selectColumnInEditModeExpression(fix, 3); // Select 'Released' column. - selectOperatorInEditModeExpression(fix, 1); // Select 'True' operator. - verifyEditModeExpressionInputStates(fix, true, true, false, true); // Third input should be disabled for unary operators. + // Verfiy there is a root group with 'And' operator line and 2 children. + const rootGroup = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + expect(rootGroup).not.toBeNull(); + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); + expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(rootGroup as HTMLElement).length).toBe(2); - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + // Verify the contnet of the first child (expression) of the root group. + QueryBuilderFunctions.verifyExpressionChipContent(fix, [1], 'Downloads', 'Greater Than', '100'); + + // Verify the content of the second child (group) of the root group. + const group = QueryBuilderFunctions.getQueryBuilderTreeItem(fix, [0]); + expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(group as HTMLElement, false).length).toBe(2); + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular'); + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 1], 'ProductName', 'Contains', 'script'); + // Verify the operator line of the child group. + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeGroupOperatorLine(fix, [0]) as HTMLElement, 'or'); + + // Close Advanced Filtering dialog. + GridFunctions.clickAdvancedFilteringCancelButton(fix); + tick(100); fix.detectChanges(); - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); + // Clear filters through API. + grid.advancedFilteringExpressionsTree = null; fix.detectChanges(); - // Verify the filter results. - expect(grid.filteredData.length).toEqual(3); - expect(grid.rowList.length).toBe(3); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('NetAdvantage'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe(null); - })); - - it('Should correctly filter by a \'date\' column through UI with unary operator.', fakeAsync(() => { - // Test prerequisites - grid.height = '800px'; - fix.detectChanges(); - tick(50); - - // Verify no filters are present. - expect(grid.filteredData).toBeNull(); - expect(grid.rowList.length).toBe(8); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - selectColumnInEditModeExpression(fix, 4); // Select 'ReleaseDate' column. - selectOperatorInEditModeExpression(fix, 9); // Select 'This Year' operator. - verifyEditModeExpressionInputStates(fix, true, true, false, true); // Third input should be disabled for unary operators. - const input = GridFunctions.getAdvancedFilteringValueInput(fix, true); - input.click(); - fix.detectChanges(); - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify the filter results. - const expectedData = fix.componentInstance.data.filter(r => - r.ReleaseDate && r.ReleaseDate.getFullYear() === (new Date()).getFullYear()); - expect(grid.filteredData.length).toEqual(expectedData.length); - expect(grid.rowList.length).toBe(expectedData.length); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe(expectedData[0].ProductName); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe(expectedData[1].ProductName); + // Verify there are not filters present and that the default text is shown. + expect(grid.advancedFilteringExpressionsTree).toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix)).toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix)).not.toBeNull(); })); - it('Should correctly filter by a \'date\' column through UI with value from calendar.', fakeAsync(() => { + it('Applying/Clearing filter through the UI should correctly update the API.', fakeAsync(() => { // Test prerequisites grid.height = '800px'; fix.detectChanges(); tick(50); - // Verify no filters are present. + // Verify the initial state of the grid and that no filters are present. + expect(grid.advancedFilteringExpressionsTree).toBeUndefined(); expect(grid.filteredData).toBeNull(); expect(grid.rowList.length).toBe(8); expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - selectColumnInEditModeExpression(fix, 4); // Select 'ReleaseDate' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator. - verifyEditModeExpressionInputStates(fix, true, true, true, false); - const input = GridFunctions.getAdvancedFilteringValueInput(fix, true); - input.click(); - fix.detectChanges(); - - // Click on 'today' item in calendar. - const calendar = GridFunctions.getAdvancedFilteringCalendar(fix); - const todayItem = calendar.querySelector('.igx-days-view__date--current'); - todayItem.firstChild.dispatchEvent(new Event('mousedown')); - tick(100); - fix.detectChanges(); - - verifyEditModeExpressionInputStates(fix, true, true, true, true); - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); - fix.detectChanges(); - - // Verify the filter results. - expect(grid.filteredData.length).toEqual(1); - expect(grid.rowList.length).toBe(1); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 0).value.toString()).toBe('8'); - - flush(); - })); - - it('Should emit the filtering event when applying filters.', fakeAsync(() => { - spyOn(grid.filtering, 'emit'); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Ensure that filtering event was emitted with expected arguments - expect(grid.filtering.emit).toHaveBeenCalledWith(jasmine.objectContaining({ - owner: grid, - filteringExpressions: grid.advancedFilteringExpressionsTree, - cancel: false - })); - })); - - it('Should cancel filtering if cancel is set to true.', fakeAsync(() => { - spyOn(grid.filtering, 'emit').and.callFake((args: IFilteringEventArgs) => { - args.cancel = true; - }); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Ensure that cancel flag is true - expect(grid.filtering.emit).toHaveBeenCalled(); - const emittedArgs: IFilteringEventArgs = (grid.filtering.emit as jasmine.Spy).calls.mostRecent().args[0]; - expect(emittedArgs.cancel).toBeTrue(); - - // Ensure that grid.filteredData is null - expect(grid.filteredData).toEqual(null); - })); - - it('Should emit the filteringDone event when applying filters.', fakeAsync(() => { - spyOn(grid.filteringDone, 'emit'); - // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Apply the filters. @@ -784,89 +506,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { tick(100); fix.detectChanges(); - expect(grid.filteringDone.emit).toHaveBeenCalledWith(grid.advancedFilteringExpressionsTree); - expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number').textContent).toContain('1'); - })); - - it('Applying/Clearing filter through the API should correctly update the UI and correctly show number of filtered columns', fakeAsync(() => { - grid.height = '800px'; - fix.detectChanges(); - tick(50); - - // Verify the initial state of the grid and that no filters are present. - expect(grid.filteredData).toBeNull(); - expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number')).toBeNull(); - - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Verify the state of the grid after filtering. - expect(grid.filteredData.length).toBe(2); - expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number').textContent).toContain('(2)'); - expect(GridFunctions.getExcelFilterIconFiltered(fix, 'ProductName')).toBeDefined(); - expect(GridFunctions.getExcelFilterIconFiltered(fix, 'Downloads')).toBeDefined(); - - // Clear filters through API. - grid.advancedFilteringExpressionsTree = null; - fix.detectChanges(); - - // Verify there are not filters present and that the default text is shown. - expect(grid.advancedFilteringExpressionsTree).toBeNull(); - expect(grid.nativeElement.querySelector('.igx-adv-filter--column-number')).toBeNull(); - })); - - it('Applying/Clearing filter through the API should correctly update the UI.', fakeAsync(() => { - // Test prerequisites - grid.height = '800px'; - fix.detectChanges(); - tick(50); - - // Verify the initial state of the grid and that no filters are present. - expect(grid.filteredData).toBeNull(); - expect(grid.rowList.length).toBe(8); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Verify the state of the grid after filtering. + // Verify the state of the grid after the filtering. + expect(grid.advancedFilteringExpressionsTree !== null && grid.advancedFilteringExpressionsTree !== undefined).toBe(true); expect(grid.filteredData.length).toBe(2); expect(grid.rowList.length).toBe(2); expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); @@ -874,894 +515,280 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verfiy there is a root group with 'And' operator line and 2 children. - const rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(rootGroup).not.toBeNull(); - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix), 'and'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup).length).toBe(2); - - // Verify the contnet of the first child (expression) of the root group. - verifyExpressionChipContent(fix, [0], 'Downloads', 'Greater Than', '100'); - - // Verify the content of the second child (group) of the root group. - const group = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, false).length).toBe(2); - verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular'); - verifyExpressionChipContent(fix, [1, 1], 'ProductName', 'Contains', 'script'); - // Verify the operator line of the child group. - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]), 'or'); - - // Close Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringCancelButton(fix); tick(100); fix.detectChanges(); - // Clear filters through API. - grid.advancedFilteringExpressionsTree = null; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); + // Clear the filters. + GridFunctions.clickAdvancedFilteringClearFilterButton(fix); + tick(100); fix.detectChanges(); - // Verify there are not filters present and that the default text is shown. + // Verify that no filters are present. expect(grid.advancedFilteringExpressionsTree).toBeNull(); - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull(); - expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).not.toBeNull(); - })); - - it('Applying/Clearing filter through the UI should correctly update the API.', fakeAsync(() => { - // Test prerequisites - grid.height = '800px'; - fix.detectChanges(); - tick(50); - - // Verify the initial state of the grid and that no filters are present. - expect(grid.advancedFilteringExpressionsTree).toBeUndefined(); expect(grid.filteredData).toBeNull(); expect(grid.rowList.length).toBe(8); expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); + })); - // Open Advanced Filtering dialog. + it('Should apply filters on Apply button click without prior Commit button click', fakeAsync(() => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Add a root 'and' group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator. - let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, '100', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Add a child 'or' group. - const addOrGroupBtn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[2]; - addOrGroupBtn.click(); - tick(100); - fix.detectChanges(); - - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Add new expression to the child group. - const addExpressionBtn = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [1], 0)[0]; - addExpressionBtn.click(); - tick(100); - fix.detectChanges(); - - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Verify the state of the grid after the filtering. - expect(grid.advancedFilteringExpressionsTree !== null && grid.advancedFilteringExpressionsTree !== undefined).toBe(true); - expect(grid.filteredData.length).toBe(2); - expect(grid.rowList.length).toBe(2); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - tick(100); - fix.detectChanges(); - - // Clear the filters. - GridFunctions.clickAdvancedFilteringClearFilterButton(fix); - tick(100); - fix.detectChanges(); - - // Verify that no filters are present. - expect(grid.advancedFilteringExpressionsTree).toBeNull(); - expect(grid.filteredData).toBeNull(); - expect(grid.rowList.length).toBe(8); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - })); - - it('Should discard the newly added group when clicking the \'close\' button of its initial condition.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Verify there is a new group and the initial buttons are not visible. - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).not.toBeNull(); - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(0); - - // Populate edit inputs - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. - - // Discard the populated expression, so the whole new group gets discarded. - GridFunctions.clickAdvancedFilteringExpressionCloseButton(fix); - fix.detectChanges(); - - // Verify there are no groups in the dialog and the initial buttons are visible. - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull(); - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix).length).toBe(2); - })); - - it('Should apply filters on Apply button click without prior Commit button click', fakeAsync(() => { - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. - - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Verify the filter results. - expect(grid.filteredData.length).toEqual(2); - expect(grid.rowList.length).toBe(2); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular'); - })); - - it('Should close the dialog on Apply button click if not all expression inputs are set', fakeAsync(() => { - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Verify the dialog is closed an no records are filtered - expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull(); - expect(grid.filteredData).toBe(null); - - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull(); - expect(grid.filteredData).toBe(null); - - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. - - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull(); - expect(grid.filteredData).toBe(null); - })); - - it('Column dropdown should contain only filterable columns.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Make the 'Downloads', 'Released' and 'ReleaseDate' columns non-filterable. - grid.getColumnByName('Downloads').filterable = false; - grid.getColumnByName('Released').filterable = false; - grid.getColumnByName('ReleaseDate').filterable = false; - grid.cdr.detectChanges(); - tick(100); - - // Click the initial 'Add and Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Open column dropdown and verify that only filterable columns are present. - GridFunctions.clickAdvancedFilteringColumnSelect(fix); - fix.detectChanges(); - const dropdownItems = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix); - expect(dropdownItems.length).toBe(3); - expect(dropdownItems[0].innerText).toBe('HeaderID'); - expect(dropdownItems[1].innerText).toBe('ProductName'); - expect(dropdownItems[2].innerText).toBe('Another Field'); - })); - - it('Operator dropdown should contain operators based on the column\'s datatype (\'string\' or \'number\').', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Add a new group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select 'string' type column ('ProductName'). - selectColumnInEditModeExpression(fix, 1); - // Open the operator dropdown and verify they are 'string' specific. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - fix.detectChanges(); - let dropdownValues: string[] = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText); - let expectedValues = ['Contains', 'Does Not Contain', 'Starts With', 'Ends With', 'Equals', - 'Does Not Equal', 'Empty', 'Not Empty', 'Null', 'Not Null']; - expect(dropdownValues).toEqual(expectedValues); - - // Close current dropdown by a random select. - GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, 0); - tick(); - fix.detectChanges(); - - // Select 'number' type column ('Downloads'). - selectColumnInEditModeExpression(fix, 2); - // Open the operator dropdown and verify they are 'number' specific. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - fix.detectChanges(); - dropdownValues = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText); - expectedValues = ['Equals', 'Does Not Equal', 'Greater Than', 'Less Than', 'Greater Than Or Equal To', - 'Less Than Or Equal To', 'Empty', 'Not Empty', 'Null', 'Not Null']; - expect(dropdownValues).toEqual(expectedValues); - })); - - it('Operator dropdown should contain operators based on the column\'s datatype (\'date\' or \'boolean\').', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Add a new group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select 'date' type column ('ReleaseDate'). - selectColumnInEditModeExpression(fix, 4); - // Open the operator dropdown and verify they are 'date' specific. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - fix.detectChanges(); - let dropdownValues: string[] = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText); - let expectedValues = ['Equals', 'Does Not Equal', 'Before', 'After', 'Today', 'Yesterday', - 'This Month', 'Last Month', 'Next Month', 'This Year', 'Last Year', - 'Next Year', 'Empty', 'Not Empty', 'Null', 'Not Null']; - expect(dropdownValues).toEqual(expectedValues); - - // Close current dropdown by a random select. - GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, 0); - tick(); - fix.detectChanges(); - - // Select 'boolean' type column ('Released'). - selectColumnInEditModeExpression(fix, 3); - // Open the operator dropdown and verify they are 'boolean' specific. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - fix.detectChanges(); - dropdownValues = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText); - expectedValues = ['All', 'True', 'False', 'Empty', 'Not Empty', 'Null', 'Not Null']; - expect(dropdownValues).toEqual(expectedValues); - })); - - it('Should not commit and close currently edited condition when the \'close\' button is clicked.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - verifyExpressionChipContent(fix, [0], 'Downloads', 'Greater Than', '100'); - - // Edit the first expression in the inner 'or' group. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0], true); // Double-click the chip - tick(200); - fix.detectChanges(); - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, '500', fix); // Type filter value. - - // Verify the edit mode container is visible. - expect(GridFunctions.getAdvancedFilteringEditModeContainer(fix)).not.toBeNull(); - - // Close the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCloseButton(fix); - fix.detectChanges(); - - // Verify the edit mode container is no longer visible. - expect(GridFunctions.getAdvancedFilteringEditModeContainer(fix)).toBeNull(); - - verifyExpressionChipContent(fix, [0], 'Downloads', 'Greater Than', '100'); - })); - - it('Should scroll the adding buttons into view when the add icon of a chip is clicked.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - for (let index = 0; index < 30; index++) { - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), - conditionName: 'equals' - }); - } - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); - - // Scroll to the top. - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - tick(50); - exprContainer.scrollTop = 0; - - // Hover the last visible expression chip - const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[9]; - expressionItem.dispatchEvent(new MouseEvent('mouseenter')); - tick(); - fix.detectChanges(); - - // Click the add icon to display the adding buttons. - GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [9]); - fix.detectChanges(); - tick(50); - - // Verify the adding buttons are in view. - const addingButtons = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0); - for (const addingButton of addingButtons) { - verifyElementIsInExpressionsContainerView(fix, addingButton); - } - })); - - it('Should scroll the newly added expression into view when the respective add button is clicked.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - for (let index = 0; index < 30; index++) { - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), - conditionName: 'equals' - }); - } - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); - - // Scroll to the top. - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - tick(50); - exprContainer.scrollTop = 0; - - // Hover the previous to last visible expression chip. - const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[8]; - expressionItem.dispatchEvent(new MouseEvent('mouseenter')); - tick(); - fix.detectChanges(); - - // Click the add icon to display the adding buttons. - GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [8]); - tick(50); - - // Click the 'add condition' button. - const addCondButton = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0]; - addCondButton.click(); - fix.detectChanges(); - - // Verify the edit mode container (the one with the editing inputs) is in view. - verifyElementIsInExpressionsContainerView(fix, GridFunctions.getAdvancedFilteringEditModeContainer(fix)); - })); - - it('Should scroll to the expression when entering its edit mode.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - for (let index = 0; index < 30; index++) { - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), - conditionName: 'equals' - }); - } - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - GridFunctions.clickAdvancedFilteringButton(fix); - fix.detectChanges(); - - // Scroll to the top. - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - tick(50); - exprContainer.scrollTop = 0; - - // Hover the last visible expression chip - const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[9]; - expressionItem.dispatchEvent(new MouseEvent('mouseenter')); - tick(); - fix.detectChanges(); - - // Click the edit icon to enter edit mode of the expression. - GridFunctions.clickAdvancedFilteringTreeExpressionChipEditIcon(fix, [9]); - tick(50); - - // Verify the edit mode container (the one with the editing inputs) is in view. - verifyElementIsInExpressionsContainerView(fix, GridFunctions.getAdvancedFilteringEditModeContainer(fix)); - })); - - it('Should keep the context menu in view when scrolling the expressions container.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - for (let index = 0; index < 20; index++) { - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), - conditionName: 'equals' - }); - } - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Scroll to the top. - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - tick(50); - exprContainer.scrollTop = 0; - - // Select the first two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - fix.detectChanges(); - - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1]); - tick(200); // Await click timeout - fix.detectChanges(); - - // Scroll to the bottom. - exprContainer.scrollTop = exprContainer.scrollHeight; - - // Verify that the context menu is correctly repositioned and in view. - const contextMenu = GridFunctions.getAdvancedFilteringContextMenu(fix); - verifyElementIsInExpressionsContainerView(fix, contextMenu); - })); - - it('Should clear all conditions and groups when the \'clear filter\' button is clicked.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify there are filters in the dialog. - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).not.toBeNull(); - expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).toBeNull(); - - // Clear the filters. - GridFunctions.clickAdvancedFilteringClearFilterButton(fix); - tick(100); - fix.detectChanges(); - - // Verify there are no filters in the dialog. - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull(); - expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix)).not.toBeNull(); - })); - - it('Should keep edited conditions and groups inside AF dialog when applying and opening it again.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan', - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Verify the current filter state. - expect(grid.filteredData.length).toBe(2); - expect(grid.rowList.length).toBe(2); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify the content of the first expression in the inner 'or' group. - verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular'); - - // Edit the first expression in the inner 'or' group. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 0], true); // Double-click the chip - tick(200); - fix.detectChanges(); - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value. - - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); - - // Apply the filters. - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Verify the new current filter state. - expect(grid.filteredData.length).toBe(3); - expect(grid.rowList.length).toBe(3); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify the content of the first expression in the inner 'or' group. - verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'a'); - })); - - it('Should not keep changes over edited conditions and groups inside AF dialog when canceling and opening it again.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'ign', fix); // Type filter value. + + GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); fix.detectChanges(); - // Verify the current filter state. - expect(grid.filteredData.length).toBe(2); + // Verify the filter results. + expect(grid.filteredData.length).toEqual(2); expect(grid.rowList.length).toBe(2); expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular'); + })); - // Open Advanced Filtering dialog. + it('Should close the dialog on Apply button click if not all expression inputs are set', fakeAsync(() => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify the content of the first expression in the inner 'or' group. - verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular'); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); - // Edit the first expression in the inner 'or' group. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 0], true); // Double-click the chip - tick(200); + GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); fix.detectChanges(); - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + // Verify the dialog is closed an no records are filtered + expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull(); + expect(grid.filteredData).toBe(null); + + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Cancel the filters. - GridFunctions.clickAdvancedFilteringCancelButton(fix); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - // Verify the new filter state remains unchanged. - expect(grid.filteredData.length).toBe(2); - expect(grid.rowList.length).toBe(2); - expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); - expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + + GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); + fix.detectChanges(); + + expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull(); + expect(grid.filteredData).toBe(null); - // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify the content of the first expression in the inner 'or' group. - verifyExpressionChipContent(fix, [1, 0], 'ProductName', 'Contains', 'angular'); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. + + GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); + fix.detectChanges(); + + expect(GridFunctions.getAdvancedFilteringComponent(fix)).toBeNull(); + expect(grid.filteredData).toBe(null); })); - it('Should not close the AF dialog when clicking outside of it.', fakeAsync(() => { + it('Column dropdown should contain only filterable columns.', fakeAsync(() => { + // Make the 'Downloads', 'Released' and 'ReleaseDate' columns non-filterable. + grid.getColumnByName('Downloads').filterable = false; + grid.getColumnByName('Released').filterable = false; + grid.getColumnByName('ReleaseDate').filterable = false; + grid.cdr.detectChanges(); + tick(100); + // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify that the Advanced Filtering dialog is opened. - expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull('Advanced Filtering dialog is not opened.'); + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - grid.nativeElement.click(); - tick(200); + // Click the initial 'Add And Group' button of the query builder. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); fix.detectChanges(); - // Verify that the Advanced Filtering dialog remains opened. - expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull('Advanced Filtering dialog is not opened.'); + // Open column dropdown and verify that only filterable columns are present. + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); + fix.detectChanges(); + const dropdownItems = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement); + expect(dropdownItems.length).toBe(3); + expect((dropdownItems[0] as HTMLElement).innerText).toBe('HeaderID'); + expect((dropdownItems[1] as HTMLElement).innerText).toBe('ProductName'); + expect((dropdownItems[2] as HTMLElement).innerText).toBe('Another Field'); })); - it('Should display the adding buttons and the cancel button when trying to add a new condition/group to existing group.', - fakeAsync(() => { + it('Should scroll the adding buttons into view when the add icon of a chip is clicked.', fakeAsync(() => { // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); + const tree = new FilteringExpressionsTree(FilteringLogic.Or); + for (let index = 0; index < 30; index++) { + tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' + }); + } grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); + GridFunctions.clickAdvancedFilteringButton(fix); fix.detectChanges(); - // Select a chip from the child group. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0, 0]); - tick(200); + // Scroll to the top. + const exprContainer = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix); + tick(50); + exprContainer.scrollTop = 0; + + // Hover the last visible expression chip + const expressionItem = fix.nativeElement.querySelectorAll(`.${QueryBuilderConstants.QUERY_BUILDER_EXPRESSION_ITEM_CLASS}`)[9]; + expressionItem.dispatchEvent(new MouseEvent('mouseenter')); + tick(); fix.detectChanges(); + // Click the add icon to display the adding buttons. - GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [0, 0]); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fix, [9], 'add'); fix.detectChanges(); + tick(50); - // Verify the adding buttons and cancel button are visible and enabled. - const buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0], 0); - expect(buttons.length).toBe(4); - for (const button of buttons) { - ControlsFunction.verifyButtonIsDisabled(button, false); + // Verify the adding buttons are in view. + const addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const addingButton of addingButtons) { + verifyElementIsInExpressionsContainerView(fix, addingButton as HTMLElement); } - - // Click the cancel button to hide the buttons. - const cancelButton = buttons[3]; - cancelButton.click(); - fix.detectChanges(); - - // Verify the adding buttons and cancel button are no longer visible. - const group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); - const childrenContainer = group.querySelector('.igx-filter-tree__expression'); - const buttonsContainers = childrenContainer.querySelectorAll(':scope > .igx-filter-tree__buttons'); - expect(buttonsContainers.length).toBe(0, 'Adding buttons are visible.'); })); - it('Should add a new condition to existing group by using add buttons.', fakeAsync(() => { + it('Should scroll the newly added expression into view when the respective add button is clicked.', fakeAsync(() => { // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); + const tree = new FilteringExpressionsTree(FilteringLogic.Or); + for (let index = 0; index < 30; index++) { + tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' + }); + } grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); + GridFunctions.clickAdvancedFilteringButton(fix); fix.detectChanges(); - // Verify group's children count before adding a new child. - let group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); - let groupDirectChildren = GridFunctions.getAdvancedFilteringTreeChildItems(group); - expect(groupDirectChildren.length).toBe(2, 'incorrect direct children count of inner group'); + // Scroll to the top. + const exprContainer = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix); + tick(50); + exprContainer.scrollTop = 0; - // Select chip from group and click the add button. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0, 0]); - tick(200); - fix.detectChanges(); - GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [0, 0]); + // Hover the previous to last visible expression chip. + const expressionItem = fix.nativeElement.querySelectorAll(`.${QueryBuilderConstants.QUERY_BUILDER_EXPRESSION_ITEM_CLASS}`)[9]; + expressionItem.dispatchEvent(new MouseEvent('mouseenter')); + tick(); fix.detectChanges(); - // Add new 'expression'. - const buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0], 0); - buttons[0].click(); + // Click the add icon to display the adding buttons. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fix, [9], 'add'); fix.detectChanges(); + tick(50); - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'some value', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + // Click the 'add condition' button. + const addCondButton = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement; + addCondButton.click(); fix.detectChanges(); - // Verify group's children count before adding a new child. - group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); - groupDirectChildren = GridFunctions.getAdvancedFilteringTreeChildItems(group); - expect(groupDirectChildren.length).toBe(3, 'incorrect direct children count of inner group'); + // Verify the edit mode container (the one with the editing inputs) is in view. + verifyElementIsInExpressionsContainerView(fix, QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, false) as HTMLElement); })); - it('Should add a new group to existing group by using add buttons.', fakeAsync(() => { + it('Should scroll to the expression when entering its edit mode.', fakeAsync(() => { // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); + const tree = new FilteringExpressionsTree(FilteringLogic.Or); + for (let index = 0; index < 30; index++) { + tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' + }); + } grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); + GridFunctions.clickAdvancedFilteringButton(fix); fix.detectChanges(); - // Verify group's children count before adding a new child. - let group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, true).length).toBe(2, - 'incorrect direct children count of group with path [0]'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, false).length).toBe(2, - 'incorrect all children count of group with path [0]'); + // Scroll to the top. + const exprContainer = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix); + tick(50); + exprContainer.scrollTop = 0; - // Select chip from group and click the add button. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0, 0]); - tick(200); - fix.detectChanges(); - GridFunctions.clickAdvancedFilteringTreeExpressionChipAddIcon(fix, [0, 0]); + // Hover the last visible expression chip + const expressionItem = fix.nativeElement.querySelectorAll(`.${QueryBuilderConstants.QUERY_BUILDER_EXPRESSION_ITEM_CLASS}`)[9]; + expressionItem.dispatchEvent(new MouseEvent('mouseenter')); + tick(); fix.detectChanges(); - // Add new 'and group'. - let buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0], 0); - buttons[1].click(); + // Click the edit icon to enter edit mode of the expression. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fix, [9], 'edit'); fix.detectChanges(); + tick(50); - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'some value', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + // Verify the edit mode container (the one with the editing inputs) is in view. + verifyElementIsInExpressionsContainerView(fix, QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, false) as HTMLElement); + })); + + it('Should keep the context menu in view when scrolling the expressions container.', fakeAsync(() => { + // Apply advanced filter through API. + const tree = new FilteringExpressionsTree(FilteringLogic.Or); + for (let index = 0; index < 20; index++) { + tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: index, condition: IgxNumberFilteringOperand.instance().condition('equals'), conditionName: 'equals' + }); + } + grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); - // Add new 'expression' to the newly added group. - buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0, 1], 0); - buttons[0].click(); + // Open Advanced Filtering dialog. + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'another value', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + // Scroll to the top. + const exprContainer = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix); + tick(50); + exprContainer.scrollTop = 0; + + // Select the first two chips. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); fix.detectChanges(); - // End the newly added group. - buttons = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [0, 1], 0); - buttons[3].click(); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); + tick(200); // Await click timeout fix.detectChanges(); - // Verify group's children count before adding a new child. - group = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, true).length).toBe(3, - 'incorrect direct children count of group with path [0]'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(group, false).length).toBe(5, - 'incorrect all children count of group with path [0]'); + // Scroll to the bottom. + exprContainer.scrollTop = exprContainer.scrollHeight; + + // Verify that the context menu is correctly repositioned and in view. + const contextMenu = QueryBuilderFunctions.getQueryBuilderContextMenus(fix)[0]; + verifyElementIsInExpressionsContainerView(fix, contextMenu.nativeElement); })); - it('Should remove a condition from an existing group by using delete icon of respective chip.', fakeAsync(() => { + it('Should clear all conditions and groups when the \'clear filter\' button is clicked.', fakeAsync(() => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ @@ -1787,109 +814,86 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify tree layout before deleting chips. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4); - - // Delete a chip and verify layout. - GridFunctions.clickAdvancedFilteringTreeExpressionChipRemoveIcon(fix, [0]); - tick(100); - fix.detectChanges(); - - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3); + // Verify there are filters in the dialog. + expect(QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix)).not.toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix)).toBeNull(); - // Delete a chip and verify layout. - GridFunctions.clickAdvancedFilteringTreeExpressionChipRemoveIcon(fix, [0, 1]); + // Clear the filters. + GridFunctions.clickAdvancedFilteringClearFilterButton(fix); tick(100); - flush(); fix.detectChanges(); - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2); - // Verify remaining chip's content. - verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular'); + // Verify there are no filters in the dialog. + expect(QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix)).toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix)).not.toBeNull(); })); - it('Should select/deselect a condition when its respective chip is clicked.', fakeAsync(() => { + it('Should keep edited conditions and groups inside AF dialog when applying and opening it again.', fakeAsync(() => { // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); + const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ + fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan' + }); + const orTree = new FilteringExpressionsTree(FilteringLogic.Or); + orTree.filteringOperands.push({ fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', ignoreCase: true }); - tree.filteringOperands.push({ + orTree.filteringOperands.push({ fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), conditionName: 'contains', ignoreCase: true }); + tree.filteringOperands.push(orTree); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); + // Verify the current filter state. + expect(grid.filteredData.length).toBe(2); + expect(grid.rowList.length).toBe(2); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); + // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify first chip is not selected. - verifyExpressionChipSelection(fix, [0], false); + // Verify the content of the first expression in the inner 'or' group. + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular'); - // Click first chip and verify it is selected. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); + // Edit the first expression in the inner 'or' group. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0, 0], true); // Double-click the chip tick(200); fix.detectChanges(); - verifyExpressionChipSelection(fix, [0], true); + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value. - // Click first chip again and verify it is not selected. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - tick(200); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); - verifyExpressionChipSelection(fix, [0], false); - })); - it('Should display edit and add buttons when hovering a chip.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - grid.advancedFilteringExpressionsTree = tree; + // Apply the filters. + GridFunctions.clickAdvancedFilteringApplyButton(fix); + tick(100); fix.detectChanges(); + // Verify the new current filter state. + expect(grid.filteredData.length).toBe(3); + expect(grid.rowList.length).toBe(3); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage'); + // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Verify actions container is not visible. (This container contains the 'edit' and the 'add' buttons.) - expect(GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, [0])) - .toBeNull('actions container is visible'); - - // Hover the first chip and verify actions container is visible. - UIInteractions.hoverElement(GridFunctions.getAdvancedFilteringTreeItem(fix, [0])); - tick(50); - fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, [0])) - .not.toBeNull('actions container is not visible'); - - // Unhover the first chip and verify actions container is not visible. - UIInteractions.unhoverElement(GridFunctions.getAdvancedFilteringTreeItem(fix, [0])); - tick(50); - fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, [0])) - .toBeNull('actions container is visible'); + // Verify the content of the first expression in the inner 'or' group. + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'a'); })); - it('Should select/deselect all child conditions and groups when clicking a group\'s operator line.', fakeAsync(() => { + it('Should not keep changes over edited conditions and groups inside AF dialog when canceling and opening it again.', fakeAsync(() => { // Apply advanced filter through API. const tree = new FilteringExpressionsTree(FilteringLogic.And); tree.filteringOperands.push({ @@ -1907,110 +911,67 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { conditionName: 'contains', ignoreCase: true }); - const andTree = new FilteringExpressionsTree(FilteringLogic.Or); - andTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'a', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - andTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 's', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push(andTree); tree.filteringOperands.push(orTree); grid.advancedFilteringExpressionsTree = tree; fix.detectChanges(); + // Verify the current filter state. + expect(grid.filteredData.length).toBe(2); + expect(grid.rowList.length).toBe(2); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); + // Open Advanced Filtering dialog. grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Click root group's operator line and verify that the root group and all of its children become selected. - let rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.click(); - tick(200); - fix.detectChanges(); - verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), true); - - // Click root group's operator line again and verify that the root group and all of its children become unselected. - rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.click(); - tick(200); - fix.detectChanges(); - verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), false); - - // Click an inner group's operator line and verify its children become selected. - GridFunctions.clickAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - tick(200); - fix.detectChanges(); - verifyChildrenSelection(GridFunctions.getAdvancedFilteringTreeItem(fix, [1]), true); + // Verify the content of the first expression in the inner 'or' group. + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular'); - // Click an inner group's operator line again and verify its children become unselected. - GridFunctions.clickAdvancedFilteringTreeGroupOperatorLine(fix, [1]); + // Edit the first expression in the inner 'or' group. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0, 0], true); // Double-click the chip tick(200); fix.detectChanges(); - verifyChildrenSelection(GridFunctions.getAdvancedFilteringTreeItem(fix, [1]), false); - })); + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a', fix); // Type filter value. - it('Should open the operator dropdown below its respective input-group.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); - // Add root group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + // Cancel the filters. + GridFunctions.clickAdvancedFilteringCancelButton(fix); tick(100); fix.detectChanges(); - // Add a new expression - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value. - // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); - fix.detectChanges(); + // Verify the new filter state remains unchanged. + expect(grid.filteredData.length).toBe(2); + expect(grid.rowList.length).toBe(2); + expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript'); + expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Some other item with Script'); - // Add another expression to root group. - const btn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0]; - btn.click(); - tick(100); + // Open Advanced Filtering dialog. + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - // Populate column input. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + // Verify the content of the first expression in the inner 'or' group. + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular'); + })); - // Open the dropdown. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - tick(50); + it('Should not close the AF dialog when clicking outside of it.', fakeAsync(() => { + // Open Advanced Filtering dialog. + grid.openAdvancedFilteringDialog(); fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringSelectDropdown(fix)).not.toBeNull('dropdown is not opened'); - // Close the dropdown. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - tick(50); - fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringSelectDropdown(fix)).toBeNull('dropdown is opened'); + // Verify that the Advanced Filtering dialog is opened. + expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull('Advanced Filtering dialog is not opened.'); - // Open the operator dropdown again. - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - tick(50); + grid.nativeElement.click(); + tick(200); fix.detectChanges(); - // Verify the operator dropdown is positioned below its respective input-group. - const dropdown: HTMLElement = GridFunctions.getAdvancedFilteringSelectDropdown(fix); - expect(GridFunctions.getAdvancedFilteringSelectDropdown(fix)).not.toBeNull('dropdown is not opened'); - - const dropdownRect = dropdown.getBoundingClientRect(); - const inputGroup: HTMLElement = GridFunctions.getAdvancedFilteringOperatorSelect(fix).querySelector('igx-input-group'); - const inputGroupRect = inputGroup.getBoundingClientRect(); - const delta = 2; - expect(Math.abs(dropdownRect.top - inputGroupRect.bottom) < delta).toBe(true, - 'incorrect vertical position of operator dropdown'); - expect(Math.abs(dropdownRect.left - inputGroupRect.left) < delta).toBe(true, - 'incorrect horizontal position of operator dropdown'); + // Verify that the Advanced Filtering dialog remains opened. + expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull('Advanced Filtering dialog is not opened.'); })); it('Should filter by cells formatted data when using FormattedValuesFilteringStrategy', fakeAsync(() => { @@ -2033,18 +994,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { fix.detectChanges(); // Add root group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Add a new expression - selectColumnInEditModeExpression(fix, 2); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, '1', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); tick(100); fix.detectChanges(); GridFunctions.clickAdvancedFilteringApplyButton(fix); @@ -2067,18 +1028,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { fix.detectChanges(); // Add root group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Add a new expression - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - const input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, '1:', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); tick(100); fix.detectChanges(); GridFunctions.clickAdvancedFilteringApplyButton(fix); @@ -2104,18 +1065,18 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { fixture.detectChanges(); // Click the initial 'Add And Group' button - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fixture, 0); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fixture, 0); tick(100); fixture.detectChanges(); // Populate edit inputs - selectColumnInEditModeExpression(fixture, 1); - selectOperatorInEditModeExpression(fixture, 2); - const input = GridFunctions.getAdvancedFilteringValueInput(fixture).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fixture, 1); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fixture, 2); + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fixture).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'ign', fixture); // Commit the populated expression - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fixture); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fixture); fixture.detectChanges(); // Apply the filters @@ -2130,12 +1091,12 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Change the grid's columns collection fixture.componentInstance.columns = [ - { field: 'ID', header: 'ID', width: '200px', type: 'string' }, - { field: 'CompanyName', header: 'Company Name', width: '200px', type: 'string'}, - { field: 'ContactName', header: 'Contact Name', width: '200px', type: 'string' }, - { field: 'ContactTitle', header: 'Contact Title', width: '200px', type: 'string' }, - { field: 'City', header: 'City', width: '200px', type: 'string' }, - { field: 'Country', header: 'Country', width: '200px', type: 'string' }, + { fieldName: 'ID', header: 'ID', width: '200px', type: 'string' }, + { fieldName: 'CompanyName', header: 'Company Name', width: '200px', type: 'string' }, + { fieldName: 'ContactName', header: 'Contact Name', width: '200px', type: 'string' }, + { fieldName: 'ContactTitle', header: 'Contact Title', width: '200px', type: 'string' }, + { fieldName: 'City', header: 'City', width: '200px', type: 'string' }, + { fieldName: 'Country', header: 'Country', width: '200px', type: 'string' }, ]; fixture.detectChanges(); flush(); @@ -2160,898 +1121,6 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { // Check for error messages in the console expect(consoleSpy).not.toHaveBeenCalled(); })); - - describe('Context Menu - ', () => { - it('Should discard added group when clicking its operator line without having a single expression.', fakeAsync(() => { - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Add initial 'and' group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Click operator line. - const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.click(); - tick(200); - fix.detectChanges(); - - // Verify group is discarded and the context menu was not opened. - expect(GridFunctions.getAdvancedFilteringTreeRootGroup(fix)).toBeNull('Group is not discarded.'); - verifyContextMenuVisibility(fix, false); - })); - - it('Selecting multiple conditions should display the (create group)/(delete filters) context menu.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify context menu is not visible. - verifyContextMenuVisibility(fix, false); - - // Select two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]); - tick(200); - fix.detectChanges(); - - // Verify context menu is visible. - verifyContextMenuVisibility(fix, true); - verifyContextMenuType(fix, false); - - // Unselect one of the two selected chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - tick(200); - fix.detectChanges(); - - // Verify context menu is no longer visible. - verifyContextMenuVisibility(fix, false); - })); - - it('Should create an \'and\' group from multiple selected conditions when respective context menu button is clicked.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout before the creation of a new group with context menu. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4); - - let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - let secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2); - - // Select two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]); - tick(200); - - // Click "Create And Group" in context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[1].click(); - tick(100); - fix.detectChanges(); - - // Verify tree layout after the creation of a new group with context menu. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(5); - - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // the new group - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [0]), 'and'); - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(false); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, false).length).toBe(2); - - secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]), 'or'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(1); - })); - - it('Should create an \'or\' group from multiple selected conditions when respective context menu button is clicked.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout before the creation of a new group with context menu. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4); - - let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - let secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2); - - // Select two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]); - tick(200); - - // Click "Create Or Group" in context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[2].click(); - tick(100); - fix.detectChanges(); - - // Verify tree layout after the creation of a new group with context menu. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(5); - - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // the new group - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [0]), 'or'); - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(false); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, false).length).toBe(2); - - secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - verifyOperatorLine(GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]), 'or'); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(1); - })); - - it('Should respect the changes of the groups applied from the context menu.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Verify the filter changes are applied. - expect(grid.filteredData.length).toEqual(0); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout before the group change through context menu. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2); - - // Select two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1]); - tick(200); - - // Click "Create Or Group" in context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[2].click(); - tick(100); - fix.detectChanges(); - - // Close dialog through API. - grid.closeAdvancedFilteringDialog(true); - tick(100); - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout after the group change through context menu. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3); - - // Verify the filter changes are applied. - expect(grid.filteredData.length).toEqual(3); - })); - - - it('Should delete all selected conditions when the \'delete filters\' option from context menu is clicked.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout before the creation of a new group with context menu. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4); - - let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - const secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2); - - // Select two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1, 1]); - tick(200); - - // Click "Delete Filters" in context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[3].click(); - tick(100); - fix.detectChanges(); - - // Verify tree layout after deleting some expressions with the context menu. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2); - - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(firstItem, false).length).toBe(1); - verifyExpressionChipContent(fix, [0, 0], 'ProductName', 'Contains', 'angular'); - })); - - it('Should show/hide group\'s context menu when clicking its operator line.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify context menu is not visible. - verifyContextMenuVisibility(fix, false); - - // Click the innner group's operator line. - const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - operatorLine.click(); - fix.detectChanges(); - - // Verify context menu is visible. - verifyContextMenuVisibility(fix, true); - verifyContextMenuType(fix, true); - - // Click the innner group's operator line again. - operatorLine.click(); - tick(); - fix.detectChanges(); - - // Verify context menu is no longer visible. - verifyContextMenuVisibility(fix, false); - })); - - it('Should change the group\'s operator when using its context menu buttons.', async () => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify current operator of inner group. - let operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - verifyOperatorLine(operatorLine, 'or'); - - // Click the innner group's operator line. - operatorLine.click(); - await wait(400); - fix.detectChanges(); - - // Click the 'and' button of the button group in the context menu. - const buttonGroup = GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix); - const andOperatorButton: any = Array.from(buttonGroup.querySelectorAll('.igx-button-group__item')) - .find((b: any) => b.textContent.toLowerCase() === 'and'); - andOperatorButton.click(); - await wait(); - fix.detectChanges(); - - // Verify new operator of inner group. - operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - verifyOperatorLine(operatorLine, 'and'); - - // Click the 'or' button of the button group in the context menu. - const orOperatorButton: any = Array.from(buttonGroup.querySelectorAll('.igx-button-group__item')) - .find((b: any) => b.textContent.toLowerCase() === 'or'); - orOperatorButton.click(); - await wait(); - fix.detectChanges(); - - // Verify new operator of inner group. - operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - verifyOperatorLine(operatorLine, 'or'); - }); - - it('Should apply changes in the group\'s operator made via its context menu buttons.', fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify current operator of inner group. - let operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - verifyOperatorLine(operatorLine, 'or'); - - // Verify the advancedFilteringExpressionsTree inner group's filteringOperand's operator - expect(grid.advancedFilteringExpressionsTree.filteringOperands.length).toEqual(2); - expect((grid.advancedFilteringExpressionsTree.filteringOperands[1] as IFilteringExpressionsTree).operator).toEqual(1); - - // Click the innner group's operator line. - operatorLine.click(); - tick(400); - fix.detectChanges(); - - // Click the 'and' button of the button group in the context menu. - const buttonGroup = GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix); - const andOperatorButton: any = Array.from(buttonGroup.querySelectorAll('.igx-button-group__item')) - .find((b: any) => b.textContent.toLowerCase() === 'and'); - andOperatorButton.click(); - fix.detectChanges(); - - // Verify new operator of inner group. - operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - verifyOperatorLine(operatorLine, 'and'); - - // Apply new advanced filtering logic - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Open the advanced filtering dialog - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify the operator of inner group should persist as "And" - operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - verifyOperatorLine(operatorLine, 'and'); - - // Verify the advancedFilteringExpressionsTree inner group's filteringOperand's operator - expect(grid.advancedFilteringExpressionsTree.filteringOperands.length).toEqual(2); - expect((grid.advancedFilteringExpressionsTree.filteringOperands[1] as IFilteringExpressionsTree).operator).toEqual(0); - })); - - it('Should ungroup the group\'s children and append them to next parent group when click \'ungroup\' from context menu.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan', - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout before the the ungrouping with context menu. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4); - - let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - const secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2); - - // Click the innner group's operator line. - const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - operatorLine.click(); - tick(400); - fix.detectChanges(); - - // Click "Ungroup" in context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[3].click(); - tick(100); - fix.detectChanges(); - - // Verify tree layout after ungrouping a group with the context menu. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(3); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3); - // Verify three expression in the root group are what remains. - for (let index = 0; index < 3; index++) { - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [index]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - } - - // Should apply this change to the advancedFilteringExpressionsTree - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Open the advanced filtering dialog - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify the layout after as above - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(3); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(3); - // Verify three expression in the root group are what remains, no inner groups - for (let index = 0; index < 3; index++) { - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [index]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - } - })); - - it('Ungroup button of the root group\'s context menu should be disabled.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click root group's operator line. - const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.click(); - tick(200); - fix.detectChanges(); - - // Verify the ungroup button is disabled. - const ungroupButton = GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[3]; - ControlsFunction.verifyButtonIsDisabled(ungroupButton); - })); - - it('Should delete the group from the tree when click \'delete\' from context menu.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan', - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify tree layout before deleting a group through context menu. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(4); - - let firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - const secondItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [1]); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, true).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(secondItem, false).length).toBe(2); - - // Click the innner group's operator line. - const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, [1]); - operatorLine.click(); - tick(400); - fix.detectChanges(); - - // Click "Delete" in context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[4].click(); - tick(100); - fix.detectChanges(); - - // Verify tree layout after deleting a group through context menu. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(1); - - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - // Should apply this change to the advancedFilteringExpressionsTree - GridFunctions.clickAdvancedFilteringApplyButton(fix); - tick(100); - fix.detectChanges(); - - // Open the advanced filtering dialog - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify the layout after as above - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); // group - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, true).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(1); - - firstItem = GridFunctions.getAdvancedFilteringTreeItem(fix, [0]); // expression - expect(firstItem.classList.contains(ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS)).toBe(true); - - // Verify the advancedFilteringExpressionsTree filteringOperands operator - expect(grid.advancedFilteringExpressionsTree.filteringOperands.length).toEqual(1); - })); - - it('Should close the context menu when clicking its close button.' , fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click root operator line to open the context menu. - const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.click(); - fix.detectChanges(); - - // Verify context menu is opened. - verifyContextMenuVisibility(fix, true); - - // Click close button of context menu. - const buttons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - buttons[0].click(); - tick(); - - // Verify context menu is closed. - verifyContextMenuVisibility(fix, false); - })); - }); - - describe('Keyboard Navigation/Interaction - ', () => { - it('Should close the context menu when pressing \'Escape\' on it.' , fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Click root operator line to open the context menu. - const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.click(); - fix.detectChanges(); - - // Verify context menu is opened. - verifyContextMenuVisibility(fix, true); - - // Press 'Escape' on the context menu. - UIInteractions.triggerKeyDownEvtUponElem('Escape', GridFunctions.getAdvancedFilteringContextMenu(fix)); - tick(); - - // Verify context menu is closed. - verifyContextMenuVisibility(fix, false); - })); - - it('Should select/deselect a condition when pressing \'Enter\' on its respective chip.' , fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify first chip is not selected. - verifyExpressionChipSelection(fix, [1], false); - - // Press 'Enter' on the second chip and verify it is selected. - UIInteractions.triggerKeyDownEvtUponElem('Enter', GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [1])); - tick(200); - fix.detectChanges(); - verifyExpressionChipSelection(fix, [1], true); - - // Press 'Enter' on the second chip again and verify it is not selected. - UIInteractions.triggerKeyDownEvtUponElem('Enter', GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [1])); - tick(200); - fix.detectChanges(); - verifyExpressionChipSelection(fix, [1], false); - })); - - it('Should remove a chip in when pressing \'Enter\' on its \'remove\' icon.' , fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.Or); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify the there are two chip expressions. - let rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(2); - expect(GridFunctions.getAdvancedFilteringTreeChildExpressions(rootGroup, true).length).toBe(2); - - // Press 'Enter' on the remove icon of the second chip. - const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, [1]); - const removeIcon = ControlsFunction.getChipRemoveButton(chip); - UIInteractions.triggerKeyDownEvtUponElem('Enter', removeIcon); - tick(200); - fix.detectChanges(); - - // Verify the there is only one chip expression. - rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - expect(GridFunctions.getAdvancedFilteringTreeChildItems(rootGroup, false).length).toBe(1); - expect(GridFunctions.getAdvancedFilteringTreeChildExpressions(rootGroup, true).length).toBe(1); - })); - - it('Should select/deselect all child conditions and groups when pressing \'Enter\' on a group\'s operator line.', - fakeAsync(() => { - // Apply advanced filter through API. - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'Downloads', searchVal: 100, condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), - conditionName: 'greaterThan' - }); - const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'angular', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - orTree.filteringOperands.push({ - fieldName: 'ProductName', searchVal: 'script', condition: IgxStringFilteringOperand.instance().condition('contains'), - conditionName: 'contains', - ignoreCase: true - }); - tree.filteringOperands.push(orTree); - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Press 'Enter' on the root group's operator line - let rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - const keyboardEvent = new KeyboardEvent('keydown', { key: 'Enter' }); - rootOperatorLine.dispatchEvent(keyboardEvent); - tick(); - fix.detectChanges(); - - // Simulate end of chip selection animation - const chipSelect = fix.nativeElement.querySelector(CHIP_SELECT_CLASS); - const transitionEvent = new TransitionEvent('transitionend', { - propertyName: 'width' - }); - chipSelect.dispatchEvent(transitionEvent); - - // Verify items are selected and context menu is opened - verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), true); - verifyContextMenuVisibility(fix, true); - - // Press 'Enter' on the root group's operator line again - rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); - rootOperatorLine.dispatchEvent(keyboardEvent); - tick(); - - // Verify items are not selected and context menu is closed - verifyChildrenSelection(GridFunctions.getAdvancedFilteringExpressionsContainer(fix), false); - verifyContextMenuVisibility(fix, false); - })); - - }); - }); describe('Localization - ', () => { @@ -3081,91 +1150,91 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringHeaderText(fix)).toBe('My advanced filter'); - expect(GridFunctions.getAdvancedFilteringHeaderLegendItemAnd(fix).innerText).toBe('My and'); - expect(GridFunctions.getAdvancedFilteringHeaderLegendItemOr(fix).innerText).toBe('My or'); - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix)[0].querySelector('span').innerText) + expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fix).trim()).toBe('My advanced filter'); + expect((QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fix) as HTMLElement).innerText).toBe('My and'); + expect((QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fix) as HTMLElement).innerText).toBe('My or'); + expect(QueryBuilderFunctions.getQueryBuilderInitialAddGroupButtons(fix)[0].querySelector('span').innerText) .toBe('My and group'); - expect(GridFunctions.getAdvancedFilteringInitialAddGroupButtons(fix)[1].querySelector('span').innerText) + expect(QueryBuilderFunctions.getQueryBuilderInitialAddGroupButtons(fix)[1].querySelector('span').innerText) .toBe('My or group'); - expect(GridFunctions.getAdvancedFilteringEmptyPrompt(fix).innerText).toBe('My initial text'); + expect((QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix) as HTMLElement).innerText).toBe('My initial text'); - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0].querySelector('span').innerText) + expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement).querySelector('span').innerText) .toBe('My condition'); - expect(GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[1].querySelector('span').innerText) + expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[1] as HTMLElement).querySelector('span').innerText) .toBe('My and group'); - expect(GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[2].querySelector('span').innerText) + expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[2] as HTMLElement).querySelector('span').innerText) .toBe('My or group'); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + let input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'angular', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); - const rootOperatorLine = GridFunctions.getAdvancedFilteringTreeRootGroupOperatorLine(fix); + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; rootOperatorLine.click(); fix.detectChanges(); const buttonGroupItems = GridFunctions.sortNativeElementsHorizontally( - Array.from(GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix) - .querySelectorAll('.igx-button-group__item-content'))); + Array.from(QueryBuilderFunctions.getQueryBuilderContextMenuButtonGroup(fix) + .querySelectorAll('.igx-button-group__item-content'))); expect(buttonGroupItems[0].textContent).toBe('My and'); expect(buttonGroupItems[1].textContent).toBe('My or'); - expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[3].querySelector('span').innerText) + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[3] as HTMLElement).querySelector('span').innerText) .toBe('My ungroup'); - expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[4].querySelector('span').innerText) + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[4] as HTMLElement).querySelector('span').innerText) .toBe('My delete'); // Close context menu. - GridFunctions.clickAdvancedFilteringContextMenuCloseButton(fix); + QueryBuilderFunctions.clickQueryBuilderContextMenuCloseButton(fix); fix.detectChanges(); // Add another expression to root group. - let btn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0]; + let btn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement; btn.click(); fix.detectChanges(); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'script', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Select two chips. - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [0]); - GridFunctions.clickAdvancedFilteringTreeExpressionChip(fix, [1]); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); tick(200); fix.detectChanges(); - expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[1].innerText).toBe('My create and group'); - expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[2].innerText).toBe('My create or group'); - expect(GridFunctions.getAdvancedFilteringContextMenuButtons(fix)[3].innerText).toBe('My delete filters'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[1] as HTMLElement).innerText).toBe('My create and group'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[2] as HTMLElement).innerText).toBe('My create or group'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[3] as HTMLElement).innerText).toBe('My delete filters'); // Close context menu. - GridFunctions.clickAdvancedFilteringContextMenuCloseButton(fix); + QueryBuilderFunctions.clickQueryBuilderContextMenuCloseButton(fix); tick(100); fix.detectChanges(); // Add an 'or' group to root group. - btn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[2]; + btn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[2] as HTMLElement; btn.click(); tick(100); fix.detectChanges(); - const endGroupButton = GridFunctions.getAdvancedFilteringTreeGroupButtons(fix, [2], 0)[3]; + const endGroupButton = QueryBuilderFunctions.getQueryBuilderTreeGroupButtons(fix, [0], 0)[3] as HTMLElement; expect(endGroupButton.querySelector('span').innerText).toBe('My end group'); })); }); @@ -3183,48 +1252,20 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { grid.openAdvancedFilteringDialog(); fix.detectChanges(); + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; + // Click the initial 'Add And Group' button. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Open column dropdown and verify that there are no column groups present. - GridFunctions.clickAdvancedFilteringColumnSelect(fix); + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); fix.detectChanges(); - const dropdownValues = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix).map((x: any) => x.innerText); + const dropdownValues = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); const expectedValues = ['ID', 'ProductName', 'Downloads', 'Released', 'ReleaseDate', 'Another Field', 'DateTimeCreated']; expect(expectedValues).toEqual(dropdownValues); })); - - it('Should correctly focus the search value input when editing the filtering expression', fakeAsync(() => { - // Open dialog through API. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - //Create dateTime filtering expression - const tree = new FilteringExpressionsTree(FilteringLogic.And); - tree.filteringOperands.push({ - fieldName: 'DateTimeCreated', searchVal: '11/11/2000 10:11:11 AM', conditionName: 'equals', condition: IgxStringFilteringOperand.instance().condition('equals') - }); - - grid.advancedFilteringExpressionsTree = tree; - fix.detectChanges(); - - // Hover the last visible expression chip - const expressionItem = fix.nativeElement.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)[0]; - expressionItem.dispatchEvent(new MouseEvent('mouseenter')); - tick(); - fix.detectChanges(); - - // Click the edit icon to enter edit mode of the expression. - GridFunctions.clickAdvancedFilteringTreeExpressionChipEditIcon(fix, [0]); - tick(); - fix.detectChanges(); - - //Check for the active element - let searchValueInput = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); - expect(document.activeElement).toBe(searchValueInput, 'The input should be the active element.'); - })); }); describe('External - ', () => { @@ -3237,32 +1278,32 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { it('Should allow hosting Advanced Filtering dialog outside of the grid.', fakeAsync(() => { // Add a root 'and' group. - GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column. - selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator. - let input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'Downloads' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator. + let input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, '100', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Add new expression to the root group. - const addExpressionBtn = GridFunctions.getAdvancedFilteringTreeRootGroupButtons(fix, 0)[0]; + const addExpressionBtn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement; addExpressionBtn.click(); tick(100); fix.detectChanges(); // Populate edit inputs. - selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. - selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. - input = GridFunctions.getAdvancedFilteringValueInput(fix).querySelector('input'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'ignite', fix); // Type filter value. // Commit the populated expression. - GridFunctions.clickAdvancedFilteringExpressionCommitButton(fix); + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); fix.detectChanges(); // Apply the filters. @@ -3346,190 +1387,15 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { expect(grid.filteredData).toBe(null); })); }); - - describe('Overlay settings - ', () => { - it('Should respect the overlay settings set in the component.', fakeAsync(() => { - const fix = TestBed.createComponent(IgxGridAdvancedFilteringOverlaySettingsComponent); - const grid: IgxGridComponent = fix.componentInstance.grid; - fix.detectChanges(); - - // Open Advanced Filtering dialog. - grid.openAdvancedFilteringDialog(); - fix.detectChanges(); - - // Verify context menu is opened. - expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull(); - - // Press 'Escape' on the context menu. - UIInteractions.triggerKeyDownEvtUponElem('Escape', GridFunctions.getRowEditingOverlay(fix)); - tick(); - fix.detectChanges(); - - // Verify context menu is opened. - expect(GridFunctions.getAdvancedFilteringComponent(fix)).not.toBeNull(); - })); - }); }); -const selectColumnInEditModeExpression = (fix, dropdownItemIndex: number) => { - GridFunctions.clickAdvancedFilteringColumnSelect(fix); - fix.detectChanges(); - GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, dropdownItemIndex); - tick(); - fix.detectChanges(); -}; - -const selectOperatorInEditModeExpression = (fix, dropdownItemIndex: number) => { - GridFunctions.clickAdvancedFilteringOperatorSelect(fix); - fix.detectChanges(); - GridFunctions.clickAdvancedFilteringSelectDropdownItem(fix, dropdownItemIndex); - tick(); - fix.detectChanges(); -}; - -/** - * Verifies the type of the operator line ('and' or 'or'). - * (NOTE: The 'operator' argument must be a string with a value that is either 'and' or 'or'.) - */ -const verifyOperatorLine = (operatorLine: HTMLElement, operator: string) => { - expect(operator === 'and' || operator === 'or').toBe(true, 'operator must be \'and\' or \'or\''); - - if (operator === 'and') { - expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS)).toBe(true, 'incorrect operator line'); - expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS)).toBe(false, 'incorrect operator line'); - } else { - expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS)).toBe(false, 'incorrect operator line'); - expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS)).toBe(true, 'incorrect operator line'); - } -}; - -const verifyOperatorLineSelection = (operatorLine: HTMLElement, shouldBeSelected: boolean) => { - expect(operatorLine.classList.contains(ADVANCED_FILTERING_OPERATOR_LINE_SELECTED_CSS_CLASS)) - .toBe(shouldBeSelected, 'incorrect selection state of the operator line'); -}; - -const verifyExpressionChipContent = (fix, path: number[], columnText: string, operatorText: string, valueText: string) => { - const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, path); - const chipSpans = GridFunctions.sortNativeElementsHorizontally(Array.from(chip.querySelectorAll('span'))); - const columnSpan = chipSpans[0]; - const operatorSpan = chipSpans[1]; - const valueSpan = chipSpans[2]; - expect(columnSpan.textContent.toLowerCase().trim()).toBe(columnText.toLowerCase(), 'incorrect chip column'); - expect(operatorSpan.textContent.toLowerCase().trim()).toBe(operatorText.toLowerCase(), 'incorrect chip operator'); - expect(valueSpan.textContent.toLowerCase().trim()).toBe(valueText.toLowerCase(), 'incorrect chip filter value'); -}; - -const verifyExpressionChipSelection = (fix, path: number[], shouldBeSelected: boolean) => { - const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, path); - verifyExpressionChipSelectionByChip(chip, shouldBeSelected); -}; - -const verifyExpressionChipSelectionByChip = (chip: HTMLElement, shouldBeSelected: boolean) => { - const chipItem = chip.querySelector('.igx-chip__item'); - if (shouldBeSelected) { - expect(chipItem.classList.contains('igx-chip__item--selected')).toBe(true, 'chip is not selected'); - expect(chipItem.querySelector(CHIP_SELECT_CLASS)).not.toBeNull(); - } else { - expect(chipItem.classList.contains('igx-chip__item--selected')).toBe(false, 'chip is selected'); - expect(chipItem.querySelector(CHIP_SELECT_CLASS)).toBeNull(); - } -}; - -/** - * Verifies that all children (operator lines and expression chips) of the provided 'parent' are selected. - */ -const verifyChildrenSelection = (parent: HTMLElement, shouldBeSelected: boolean) => { - const allOperatorLines: any[] = Array.from(parent.querySelectorAll('.igx-filter-tree__line')); - const allExpressionChips: any[] = Array.from(parent.querySelectorAll(`.${ADVANCED_FILTERING_EXPRESSION_ITEM_CLASS}`)); - for (const operatorLine of allOperatorLines) { - verifyOperatorLineSelection(operatorLine, shouldBeSelected); - } - for (const expressionChip of allExpressionChips) { - verifyExpressionChipSelectionByChip(expressionChip, shouldBeSelected); - } -}; - -const verifyEditModeExpressionInputStates = (fix, - columnSelectEnabled: boolean, - operatorSelectEnabled: boolean, - valueInputEnabled: boolean, - commitButtonEnabled: boolean) => { - // Verify the column select state. - const columnInputGroup = GridFunctions.getAdvancedFilteringColumnSelect(fix).querySelector('igx-input-group'); - expect(!columnInputGroup.classList.contains('igx-input-group--disabled')).toBe(columnSelectEnabled, - 'incorrect column select state'); - - // Verify the operator select state. - const operatorInputGroup = GridFunctions.getAdvancedFilteringOperatorSelect(fix).querySelector('igx-input-group'); - expect(!operatorInputGroup.classList.contains('igx-input-group--disabled')).toBe(operatorSelectEnabled, - 'incorrect operator select state'); - - // Verify the value input state. - const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix); - const valueInputGroup = GridFunctions.sortNativeElementsHorizontally( - Array.from(editModeContainer.querySelectorAll('igx-input-group')))[2]; - expect(!valueInputGroup.classList.contains('igx-input-group--disabled')).toBe(valueInputEnabled, - 'incorrect value input state'); - - // Verify commit expression button state - const commitButton = GridFunctions.getAdvancedFilteringExpressionCommitButton(fix); - ControlsFunction.verifyButtonIsDisabled(commitButton, !commitButtonEnabled); - - - // Verify close expression button is enabled. - const closeButton = GridFunctions.getAdvancedFilteringExpressionCloseButton(fix); - ControlsFunction.verifyButtonIsDisabled(closeButton, false); -}; - -const verifyEditModeExpressionInputValues = (fix, - columnText: string, - operatorText: string, - valueText: string) => { - const columnInput = GridFunctions.getAdvancedFilteringColumnSelect(fix).querySelector('input'); - const operatorInput = GridFunctions.getAdvancedFilteringColumnSelect(fix).querySelector('input'); - const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix); - const valueInput = GridFunctions.sortNativeElementsHorizontally(Array.from(editModeContainer.querySelectorAll('input')))[2]; - expect(columnInput.value).toBe(columnText); - expect(operatorInput.value).toBe(operatorText); - expect(valueInput.value).toBe(valueText); -}; const verifyElementIsInExpressionsContainerView = (fix, element: HTMLElement) => { const elementRect = element.getBoundingClientRect(); - const exprContainer: HTMLElement = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); + const exprContainer: HTMLElement = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix) as HTMLElement; const exprContainerRect = exprContainer.getBoundingClientRect(); expect(elementRect.top >= exprContainerRect.top).toBe(true, 'top is not in view'); expect(elementRect.bottom <= exprContainerRect.bottom).toBe(true, 'bottom is not in view'); expect(elementRect.left >= exprContainerRect.left).toBe(true, 'left is not in view'); expect(elementRect.right <= exprContainerRect.right).toBe(true, 'right is not in view'); }; - -const verifyContextMenuVisibility = (fix, shouldBeVisible: boolean) => { - const contextMenu: HTMLElement = GridFunctions.getAdvancedFilteringContextMenu(fix); - const contextMenuRect = contextMenu.getBoundingClientRect(); - expect(contextMenu.classList.contains('igx-toggle--hidden')).toBe(!shouldBeVisible, 'incorrect context menu visibility'); - expect(contextMenuRect.width === 0 && contextMenuRect.height === 0).toBe(!shouldBeVisible, 'incorrect context menu dimensions'); -}; - -/** - * Verifies the type of the context menu (menu for specific group or menu for combining expressions). - * If contextual group is expected, the context menu should contain buttons for operator change, ungroup and delete. - * If contextual group is not expected, the context menu should contain buttons for creating new group by combining expressions. - */ -const verifyContextMenuType = (fix, shouldBeContextualGroup: boolean) => { - const contextMenuButtons = GridFunctions.getAdvancedFilteringContextMenuButtons(fix); - expect(GridFunctions.getAdvancedFilteringContextMenuButtonGroup(fix) !== null).toBe(shouldBeContextualGroup); - - if (shouldBeContextualGroup) { - expect(contextMenuButtons.length).toBe(5, 'incorrect buttons count in context menu'); - expect(contextMenuButtons[0].innerText.toLowerCase()).toBe('close'); - expect(contextMenuButtons[3].querySelector('span').innerText.toLowerCase()).toBe('ungroup'); - expect(contextMenuButtons[4].querySelector('span').innerText.toLowerCase()).toBe('delete'); - } else { - expect(contextMenuButtons.length).toBe(4, 'incorrect buttons count in context menu'); - expect(contextMenuButtons[0].innerText.toLowerCase()).toBe('close'); - expect(contextMenuButtons[1].innerText.toLowerCase().trim()).toBe('create "and" group'); - expect(contextMenuButtons[2].innerText.toLowerCase().trim()).toBe('create "or" group'); - expect(contextMenuButtons[3].innerText.toLowerCase().trim()).toBe('delete filters'); - } -}; diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.ts new file mode 100644 index 00000000000..0b25c955e60 --- /dev/null +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.ts @@ -0,0 +1,903 @@ +import { DebugElement } from '@angular/core'; +import { ComponentFixture, tick } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { FilteringExpressionsTree, FilteringLogic, IgxStringFilteringOperand, IgxBooleanFilteringOperand, IgxNumberFilteringOperand, IgxIconComponent, IgxDateFilteringOperand, IgxChipComponent } from 'igniteui-angular'; +import { ControlsFunction } from '../test-utils/controls-functions.spec'; +import { UIInteractions } from '../test-utils/ui-interactions.spec'; + +export const QueryBuilderConstants = { + QUERY_BUILDER_CLASS: 'igx-query-builder', + QUERY_BUILDER_HEADER: 'igx-query-builder__header', + QUERY_BUILDER_TREE: 'igx-query-builder-tree', + QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS: 'igx-filter-tree__line--and', + QUERY_BUILDER_OPERATOR_LINE_OR_CSS_CLASS: 'igx-filter-tree__line--or', + QUERY_BUILDER_OPERATOR_LINE_SELECTED_CSS_CLASS: 'igx-filter-tree__line--selected', + CSS_CLASS_DROPDOWN_LIST_SCROLL: 'igx-drop-down__list-scroll', + CHIP_SELECT_CLASS: '.igx-chip__select', + QUERY_CONTEXT_MENU: 'igx-filter-contextual-menu', + QUERY_BUILDER_BODY: 'igx-query-builder__main', + QUERY_BUILDER_EXPRESSION_ITEM_CLASS: 'igx-filter-tree__expression-item' +} + +export const SampleEntities = [ + { + name: 'Products', fields: [ + { field: 'Id', dataType: 'number' }, + { field: 'ProductName', dataType: 'string' }, + { field: 'OrderId', dataType: 'number' }, + { field: 'Released', dataType: 'boolean' } + ] + }, + { + name: 'Orders', fields: [ + { field: 'OrderId', dataType: 'number' }, + { field: 'OrderName', dataType: 'string' }, + { field: 'OrderDate', dataType: 'date' } + ] + } +]; + +export class QueryBuilderFunctions { + public static generateExpressionTree(): FilteringExpressionsTree { + const innerTree = new FilteringExpressionsTree(FilteringLogic.And, null, 'Products', ['Id']); + innerTree.filteringOperands.push({ + fieldName: 'ProductName', + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', + searchVal: 'a' + }); + innerTree.filteringOperands.push({ + fieldName: 'Released', + condition: IgxBooleanFilteringOperand.instance().condition('true'), + conditionName: 'true', + }); + + const tree = new FilteringExpressionsTree(FilteringLogic.And, null, 'Orders', ['*']); + tree.filteringOperands.push({ + fieldName: 'OrderId', + condition: IgxStringFilteringOperand.instance().condition('in'), + conditionName: 'in', + searchTree: innerTree + }); + tree.filteringOperands.push({ + fieldName: 'OrderId', + condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', + searchVal: 3, + ignoreCase: true + }); + tree.filteringOperands.push({ + fieldName: 'OrderDate', + condition: IgxDateFilteringOperand.instance().condition('after'), + conditionName: 'after', + searchVal: new Date() + }); + return tree; + } + + public static getQueryBuilderHeader(fix: ComponentFixture) { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const header = queryBuilderElement.querySelector(`.${QueryBuilderConstants.QUERY_BUILDER_HEADER}`); + return header; + } + + public static getQueryBuilderHeaderText(fix: ComponentFixture) { + const header = QueryBuilderFunctions.getQueryBuilderHeader(fix); + const title = header.querySelector('.ig-typography__h6'); + return title.textContent; + } + + public static getQueryBuilderHeaderLegendItemAnd(fix: ComponentFixture) { + const header = QueryBuilderFunctions.getQueryBuilderHeader(fix); + const andLegendItem = header.querySelector('.igx-builder-legend__item--and'); + return andLegendItem; + } + + public static getQueryBuilderHeaderLegendItemOr(fix: ComponentFixture) { + const header = QueryBuilderFunctions.getQueryBuilderHeader(fix); + const orLegendItem = header.querySelector('.igx-builder-legend__item--or'); + return orLegendItem; + } + + public static getQueryBuilderEmptyPrompt(fix: ComponentFixture) { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const emptyPrompt = queryBuilderElement.querySelector('.igx-filter-empty'); + return emptyPrompt; + } + + /** + * Get the expressions container that contains all groups and expressions. + */ + public static getQueryBuilderExpressionsContainer(fix: ComponentFixture, level = 0) { + const searchClass = `${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-${level}` + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${searchClass}`))[0].nativeElement; + const exprContainer = queryBuilderElement.querySelector('.igx-query-builder__main'); + return exprContainer; + } + + /** + * Get the initial group adding buttons when the dialog does not contain any filters. + */ + public static getQueryBuilderInitialAddGroupButtons(fix: ComponentFixture, level = 0) { + const exprContainer = this.getQueryBuilderExpressionsContainer(fix, level); + const initialButtons = Array.from(exprContainer.querySelectorAll('button')); + return initialButtons; + } + + public static getQueryBuilderAllGroups(fix: ComponentFixture): any[] { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const allGroups = Array.from(QueryBuilderFunctions.getQueryBuilderTreeChildGroups(queryBuilderElement, false)); + return allGroups; + } + + /** + * Get the root group. + */ + public static getQueryBuilderTreeRootGroup(fix: ComponentFixture, level = 0) { + const exprContainer = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix, level); + const rootGroup = exprContainer.querySelector(':scope > .igx-filter-tree'); + return rootGroup; + } + + /** + * Get all child groups of the given 'group' by specifying whether to include its direct child groups only + * or all of its child groups in the hierarchy. (NOTE: Expressions do not have children!) + */ + public static getQueryBuilderTreeChildGroups(group: HTMLElement, directChildrenOnly = true) { + const pattern = directChildrenOnly ? ':scope > .igx-filter-tree' : '.igx-filter-tree'; + const childrenContainer = group.querySelector('.igx-filter-tree__expression'); + const childGroups = Array.from(childrenContainer.querySelectorAll(pattern)); + return childGroups; + } + + /** + * Get all child expressions of the given 'group' by specifying whether to include its direct child expressions only + * or all of its child expressions in the hierarchy. + */ + public static getQueryBuilderTreeChildExpressions(group: HTMLElement, directChildrenOnly = true) { + const pattern = directChildrenOnly ? ':scope > .igx-filter-tree__expression-item' : '.igx-filter-tree__expression-item'; + const childrenContainer = group.querySelector('.igx-filter-tree__expression'); + const childExpressions = Array.from(childrenContainer.querySelectorAll(pattern)); + return childExpressions; + } + + /** + * Get all child groups and expressions of the given 'group' by specifying whether to include its + * direct child groups and expressions only or all of its child groups and expressions in the hierarchy. + */ + public static getQueryBuilderTreeChildItems(group: HTMLElement, directChildrenOnly = true) { + const childGroups = Array.from(QueryBuilderFunctions.getQueryBuilderTreeChildGroups(group, directChildrenOnly)); + const childExpressions = Array.from(QueryBuilderFunctions.getQueryBuilderTreeChildExpressions(group, directChildrenOnly)); + return childGroups.concat(childExpressions); + } + + /** + * Get a specific item from the tree (could be a group or an expression) + * by specifying its hierarchical path (not including the root group). + * (Example: [2 ,1] will first get the third item of the root group, + * and then it will get the second item of the root group's third item.) + * (NOTE: Only the items that are groups have children.) + * The returned element is the one that has been gotten last. + */ + public static getQueryBuilderTreeItem(fix: ComponentFixture, + path: number[], + level = 0) { + let node = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix, level); + for (const pos of path) { + const directChildren = QueryBuilderFunctions.getQueryBuilderTreeChildItems(node as HTMLElement, true); + node = directChildren[pos]; + } + return node; + } + + /** + * Get the operator line of the root group. + */ + public static getQueryBuilderTreeRootGroupOperatorLine(fix: ComponentFixture) { + const rootGroup = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + const directOperatorLine = rootGroup.querySelector(':scope > .igx-filter-tree__line'); + return directOperatorLine; + } + + /** + * Get the operator line of the group that is located on the provided 'path'. + */ + public static getQueryBuilderTreeGroupOperatorLine(fix: ComponentFixture, path: number[]) { + const group = QueryBuilderFunctions.getQueryBuilderTreeItem(fix, path); + const directOperatorLine = group.querySelector(':scope > .igx-filter-tree__line'); + return directOperatorLine; + } + + public static getQueryBuilderEditModeContainer(fix: ComponentFixture, entityContainer = true, level = 0) { + const exprContainer = QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix, level); + const editModeContainers = Array.from(exprContainer.querySelectorAll('.igx-filter-tree__inputs')); + const entityEditModeContainer = editModeContainers.find(container => container.querySelector('igx-combo')); + const conditionEditModeContainer = editModeContainers.find(container => container.querySelector('igx-select') && !container.querySelector('igx-combo')); + return entityContainer ? entityEditModeContainer : conditionEditModeContainer; + } + + public static getQueryBuilderEntitySelect(fix: ComponentFixture, level = 0) { + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, true, level); + const entitySelect = editModeContainer.querySelector('igx-select'); + return entitySelect; + } + + public static getQueryBuilderFieldsCombo(fix: ComponentFixture, level = 0) { + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, true, level); + const fieldCombo = editModeContainer.querySelector('igx-combo'); + return fieldCombo; + } + + public static getQueryBuilderColumnSelect(fix: ComponentFixture, level = 0) { + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, false, level); + const selects = Array.from(editModeContainer.querySelectorAll('igx-select')); + const columnSelect = selects[0]; + return columnSelect; + } + + public static getQueryBuilderOperatorSelect(fix: ComponentFixture, level = 0) { + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, false, level); + const selects = Array.from(editModeContainer.querySelectorAll('igx-select')); + const operatorSelect = selects[1]; + return operatorSelect; + } + + public static getQueryBuilderValueInput(fix: ComponentFixture, dateType = false, level = 0) { + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, false, level); + const input = dateType ? + editModeContainer.querySelector('igx-date-picker').querySelector('input') : + Array.from(editModeContainer.querySelectorAll('igx-input-group'))[2]; + return input; + } + + public static getQueryBuilderExpressionCommitButton(fix: ComponentFixture, level = 0) { + const actionButtons = fix.debugElement.queryAll(By.css('.igx-filter-tree__inputs-actions > button')); + const commitButton = actionButtons.filter((el: DebugElement) => { + const icon = el.query(By.directive(IgxIconComponent)).componentInstance; + return icon.name === 'check'; + }); + + return commitButton[level].nativeElement; + } + + public static getQueryBuilderExpressionCloseButton(fix: ComponentFixture, level = 0) { + const actionButtons = fix.debugElement.queryAll(By.css('.igx-filter-tree__inputs-actions > button')); + const closeButton = actionButtons.filter((el: DebugElement) => { + const icon = el.query(By.directive(IgxIconComponent)).componentInstance; + return icon.name === 'close'; + }); + + return closeButton[level].nativeElement; + } + + /** + * Get the adding buttons and the cancel button of the root group by specifying the + * index position of the buttons container. + */ + public static getQueryBuilderTreeRootGroupButtons(fix: ComponentFixture, buttonsIndex: number) { + const group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + const childrenContainer = group.querySelector('.igx-filter-tree__expression'); + const buttonsContainers = Array.from(childrenContainer.querySelectorAll('.igx-filter-tree__buttons')); + const buttonsContainer: any = buttonsContainers[buttonsIndex]; + const buttons = Array.from(buttonsContainer.querySelectorAll('button')); + return buttons; + } + + public static getQueryBuilderOutlet(queryBuilderElement: HTMLElement) { + const outlet = queryBuilderElement.querySelector(':scope > .igx-query-builder__outlet'); + return outlet; + } + + public static getQueryBuilderSelectDropdown(queryBuilderElement: HTMLElement) { + const outlet = QueryBuilderFunctions.getQueryBuilderOutlet(queryBuilderElement); + const selectDropdown = outlet.querySelector(`.${QueryBuilderConstants.CSS_CLASS_DROPDOWN_LIST_SCROLL}`); + return selectDropdown; + } + + public static getQueryBuilderSelectDropdownItems(queryBuilderElement: HTMLElement) { + const selectDropdown = QueryBuilderFunctions.getQueryBuilderSelectDropdown(queryBuilderElement); + const items = Array.from(selectDropdown.querySelectorAll('.igx-drop-down__item')); + return items; + } + + public static getQueryBuilderCalendar(fix: ComponentFixture) { + const calendar = fix.debugElement.queryAll(By.css(`.igx-calendar`))[0].nativeElement; + return calendar; + } + + /** + * Get the underlying chip of the expression that is located on the provided 'path'. + */ + public static getQueryBuilderTreeExpressionChip(fix: ComponentFixture, path: number[], level = 0) { + const treeItem = QueryBuilderFunctions.getQueryBuilderTreeItem(fix, path, level); + const chip = treeItem.querySelector('igx-chip'); + return chip; + } + + /** + * Get the action icons ('edit' and 'add') of the expression that is located on the provided 'path'. + */ + public static getQueryBuilderTreeExpressionActionsContainer(fix: ComponentFixture, path: number[]) { + const treeItem = QueryBuilderFunctions.getQueryBuilderTreeItem(fix, path); + const actionsContainer = treeItem.querySelector('.igx-filter-tree__expression-actions'); + return actionsContainer; + } + + /** + * Get the specified icon (add, edit, close) of the expression that is located on the provided 'path'. + */ + public static getQueryBuilderTreeExpressionIcon(fix: ComponentFixture, path: number[], iconType: string) { + const actionsContainer = QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, path); + const icons = Array.from(actionsContainer.querySelectorAll('igx-icon')); + return icons.find((icon: any) => icon.innerText === iconType) as any; + } + + /** + * Get the adding buttons and the cancel button of a group by specifying the + * path of the group and the index position of the buttons container. + * (NOTE: The buttons are returned in an array and are sorted in ascending order based on 'X' value.) + */ + public static getQueryBuilderTreeGroupButtons(fix: ComponentFixture, path: number[], buttonsIndex: number) { + const group = QueryBuilderFunctions.getQueryBuilderTreeItem(fix, path); + const childrenContainer = group.querySelector('.igx-filter-tree__expression'); + const buttonsContainers = Array.from(childrenContainer.querySelectorAll(':scope > .igx-filter-tree__buttons')); + const buttonsContainer: any = buttonsContainers[buttonsIndex]; + const buttons = Array.from(buttonsContainer.querySelectorAll('button')); + return buttons; + } + + public static getQueryBuilderContextMenus(fix: ComponentFixture) { + return fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_CONTEXT_MENU}`)); + } + + public static getQueryBuilderGroupContextMenuButton(contextMenu: DebugElement, buttonContent: string) { + return contextMenu.queryAll(By.css('.igx-button')).find(b => b.nativeElement.innerText.split("\n").pop().toLowerCase() === buttonContent.toLowerCase()) as DebugElement + } + + public static getQueryBuilderContextMenuButtons(fix: ComponentFixture) { + const contextMenu = Array.from(QueryBuilderFunctions.getQueryBuilderContextMenus(fix))[0].nativeElement; + const buttons = Array.from(contextMenu.querySelectorAll('button')); + return buttons; + } + + public static getQueryBuilderContextMenuButtonGroup(fix: ComponentFixture) { + const contextMenu = Array.from(QueryBuilderFunctions.getQueryBuilderContextMenus(fix))[0].nativeElement; + const buttonGroup = contextMenu.querySelector('igx-buttongroup'); + return buttonGroup; + } + + public static getQueryBuilderContextMenuCloseButton(fix: ComponentFixture, path = 0) { + const contextMenu = Array.from(QueryBuilderFunctions.getQueryBuilderContextMenus(fix))[path].nativeElement;; + const buttons = Array.from(contextMenu.querySelectorAll('button')); + const closeButton: any = buttons.find((b: any) => b.innerText.toLowerCase() === 'close'); + return closeButton; + } + + /* + * Get tabbable elements in a container element. Result is returned as node elements ordered they way they will be tabbed + */ + public static getTabbableElements(inElement: HTMLElement) { + const focusableElements = + 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'; + + return Array.prototype.filter.call( + inElement.querySelectorAll(focusableElements), + element => { + return (element.offsetWidth > 0 || element.offsetHeight > 0); + } + ); + } + + public static clickQueryBuilderInitialAddGroupButton(fix: ComponentFixture, buttonIndex: number, level = 0) { + const exprContainer = this.getQueryBuilderInitialAddGroupButtons(fix, level); + const andOrAddGroupButton = exprContainer[buttonIndex] as HTMLElement; + andOrAddGroupButton.click(); + } + + /** + * Click the entity select for the expression that is currently in edit mode. + */ + public static clickQueryBuilderEntitySelect(fix: ComponentFixture, level = 0) { + const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('igx-input-group') as HTMLElement; + entityInputGroup.click(); + } + + /** + * Click the fields combo for the expression that is currently in edit mode. + */ + public static clickQueryBuilderFieldsCombo(fix: ComponentFixture, level = 0) { + const fieldInputGroup = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fix, level).querySelector('igx-input-group') as HTMLElement; + fieldInputGroup.click(); + } + + /** + * Click the column select for the expression that is currently in edit mode. + */ + public static clickQueryBuilderColumnSelect(fix: ComponentFixture, level = 0) { + const columnInputGroup = QueryBuilderFunctions.getQueryBuilderColumnSelect(fix, level).querySelector('igx-input-group') as HTMLElement; + columnInputGroup.click(); + } + + /** + * Click the operator select for the expression that is currently in edit mode. + */ + public static clickQueryBuilderOperatorSelect(fix: ComponentFixture, level = 0) { + const operatorInputGroup = QueryBuilderFunctions.getQueryBuilderOperatorSelect(fix, level).querySelector('igx-input-group') as HTMLElement + operatorInputGroup.click(); + } + + /** + * Click the value input for the expression that is currently in edit mode. + * (NOTE: The value input could be either an input group or a date picker.) + */ + public static clickQueryBuilderValueInput(fix: ComponentFixture, dateType = false) { + // Could be either an input group or a date picker. + const valueInput = QueryBuilderFunctions.getQueryBuilderValueInput(fix, dateType) as HTMLElement; + valueInput.click(); + } + + /** + * Click the the select dropdown's element that is positioned at the specified 'index'. + * (NOTE: This method presumes that the select dropdown is already opened.) + */ + public static clickQueryBuilderSelectDropdownItem(queryBuilderElement: HTMLElement, index: number) { + const selectDropdownItems = Array.from(QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement)); + const item = selectDropdownItems[index] as HTMLElement; + item.click(); + } + + /** + * Click the commit button of the expression that is currently in edit mode. + */ + public static clickQueryBuilderExpressionCommitButton(fix: ComponentFixture, level = 0) { + const commitButton = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, level); + commitButton.click(); + } + + /** + * (Double)Click the underlying chip of the expression that is located on the provided 'path'. + */ + public static clickQueryBuilderTreeExpressionChip(fix: ComponentFixture, path: number[], dblClick = false, level = 0) { + const chip = QueryBuilderFunctions.getQueryBuilderTreeExpressionChip(fix, path, level) as HTMLElement; + if (dblClick) { + chip.dispatchEvent(new MouseEvent('dblclick')); + } else { + chip.click(); + } + } + + /** + * Click the remove icon of the expression that is located on the provided 'path'. + */ + public static clickQueryBuilderTreeExpressionChipRemoveIcon(fix: ComponentFixture, path: number[]) { + const chip = QueryBuilderFunctions.getQueryBuilderTreeExpressionChip(fix, path) as HTMLElement; + ControlsFunction.clickChipRemoveButton(chip); + } + + /** + * Click the specified icon (add, edit, close )of the expression that is located on the provided 'path'. + */ + public static clickQueryBuilderTreeExpressionChipIcon(fix: ComponentFixture, path: number[], iconType: string) { + const chipIcon = QueryBuilderFunctions.getQueryBuilderTreeExpressionIcon(fix, path, iconType); + chipIcon.click(); + } + + /** + * Click the operator line of the group that is located on the provided 'path'. + */ + public static clickQueryBuilderTreeGroupOperatorLine(fix: ComponentFixture, path: number[]) { + const operatorLine = QueryBuilderFunctions.getQueryBuilderTreeGroupOperatorLine(fix, path) as HTMLElement; + operatorLine.click(); + } + + public static clickQueryBuilderContextMenuCloseButton(fix: ComponentFixture, path = 0) { + const contextMenuCloseButton = QueryBuilderFunctions.getQueryBuilderContextMenuCloseButton(fix, path); + contextMenuCloseButton.click(); + } + + /* + * Hit a keyboard button upon element, wait for the desired time and detect changes + */ + //TODO maybe move to more commonly used class + public static hitKeyUponElementAndDetectChanges(fix: ComponentFixture, key: string, elem: HTMLElement, wait: number = null) { + UIInteractions.triggerKeyDownEvtUponElem(key, elem, true); + tick(wait); + fix.detectChanges(); + } + + /** + * Verifies the type of the operator line ('and' or 'or'). + * (NOTE: The 'operator' argument must be a string with a value that is either 'and' or 'or'.) + */ + public static verifyOperatorLine(operatorLine: HTMLElement, operator: string) { + expect(operator === 'and' || operator === 'or').toBe(true, 'operator must be \'and\' or \'or\''); + + if (operator === 'and') { + expect(operatorLine.classList.contains(QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS)).toBe(true, 'incorrect operator line'); + expect(operatorLine.classList.contains(QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_OR_CSS_CLASS)).toBe(false, 'incorrect operator line'); + } else { + expect(operatorLine.classList.contains(QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS)).toBe(false, 'incorrect operator line'); + expect(operatorLine.classList.contains(QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_OR_CSS_CLASS)).toBe(true, 'incorrect operator line'); + } + } + + public static verifyOperatorLineSelection(operatorLine: HTMLElement, shouldBeSelected: boolean) { + expect(operatorLine.classList.contains(QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_SELECTED_CSS_CLASS)) + .toBe(shouldBeSelected, 'incorrect selection state of the operator line'); + } + + public static verifyEditModeQueryExpressionInputStates(fix, + entitySelectEnabled: boolean, + fieldComboEnabled: boolean, + columnSelectEnabled: boolean, + operatorSelectEnabled: boolean, + valueInputEnabled: boolean, + commitButtonEnabled: boolean, + level = 0) { + // Verify the entity select state. + const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('igx-input-group'); + expect(!entityInputGroup.classList.contains('igx-input-group--disabled')).toBe(entitySelectEnabled, + 'incorrect entity select state'); + // Verify the fields combo state. + const fieldInputGroup = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fix, level).querySelector('igx-input-group'); + expect(!fieldInputGroup.classList.contains('igx-input-group--disabled')).toBe(fieldComboEnabled, + 'incorrect fields combo state'); + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, columnSelectEnabled, operatorSelectEnabled, valueInputEnabled, commitButtonEnabled, level); + }; + + public static verifyEditModeExpressionInputStates(fix, + columnSelectEnabled: boolean, + operatorSelectEnabled: boolean, + valueInputEnabled: boolean, + commitButtonEnabled: boolean, + level = 0) { + // Verify the column select state. + const columnInputGroup = QueryBuilderFunctions.getQueryBuilderColumnSelect(fix, level).querySelector('igx-input-group'); + expect(!columnInputGroup.classList.contains('igx-input-group--disabled')).toBe(columnSelectEnabled, + 'incorrect column select state'); + + // Verify the operator select state. + const operatorInputGroup = QueryBuilderFunctions.getQueryBuilderOperatorSelect(fix, level).querySelector('igx-input-group'); + expect(!operatorInputGroup.classList.contains('igx-input-group--disabled')).toBe(operatorSelectEnabled, + 'incorrect operator select state'); + + // Verify the value input state. + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fix, false, level); + const valueInputGroup = Array.from(editModeContainer.querySelectorAll('igx-input-group'))[2]; + expect(!valueInputGroup.classList.contains('igx-input-group--disabled')).toBe(valueInputEnabled, + 'incorrect value input state'); + + // Verify commit expression button state + const commitButton = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, level); + ControlsFunction.verifyButtonIsDisabled(commitButton, !commitButtonEnabled); + + // Verify close expression button is enabled. + const closeButton = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix, level); + ControlsFunction.verifyButtonIsDisabled(closeButton, false); + }; + + public static verifyQueryEditModeExpressionInputValues(fix, + entityText: string, + fieldsText: string, + columnText: string, + operatorText: string, + valueText: string, + level = 0) { + const entityInput = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('input'); + const fieldInput = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fix, level).querySelector('input'); + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, columnText, operatorText, valueText, level); + expect(entityInput.value).toBe(entityText); + expect(fieldInput.value).toBe(fieldsText); + }; + + public static verifyEditModeExpressionInputValues(fix, + columnText: string, + operatorText: string, + valueText: string, + level = 0) { + const columnInput = QueryBuilderFunctions.getQueryBuilderColumnSelect(fix, level).querySelector('input'); + const operatorInput = QueryBuilderFunctions.getQueryBuilderOperatorSelect(fix, level).querySelector('input'); + const valueInput = QueryBuilderFunctions.getQueryBuilderValueInput(fix, false, level).querySelector('input') as HTMLInputElement; + expect(columnInput.value).toBe(columnText); + expect(operatorInput.value).toBe(operatorText); + expect(valueInput.value).toBe(valueText); + }; + + public static verifyGroupContextMenuVisibility = (fix: ComponentFixture, shouldBeVisible: boolean) => { + if (shouldBeVisible) { + const wrapper = fix.debugElement.queryAll(By.css('.igx-overlay__wrapper')); + expect(wrapper.length).toBeGreaterThan(0, 'context menu wrapper missing'); + const contextMenu = wrapper[0].nativeElement.querySelector('.igx-filter-contextual-menu'); + const contextMenuRect = contextMenu.getBoundingClientRect(); + expect(contextMenu.classList.contains('igx-toggle--hidden')).toBe(false, 'incorrect context menu visibility'); + expect(contextMenuRect.width === 0 && contextMenuRect.height === 0).toBe(false, 'incorrect context menu dimensions'); + } else { + const wrapper = fix.debugElement.queryAll(By.css('.igx-overlay__wrapper')); + expect(wrapper.length).toBeLessThanOrEqual(0); + } + }; + + + public static verifyChipSelectedState = (chip: HTMLElement, shouldBeSelected: boolean) => { + const chipItem = chip.querySelector('.igx-chip__item'); + if (shouldBeSelected) { + expect(chipItem.classList.contains('igx-chip__item--selected')).toBe(true, "Chip should have been selected"); + expect(chipItem.querySelector(QueryBuilderConstants.CHIP_SELECT_CLASS)).not.toBeNull(); + } else { + expect(chipItem.classList.contains('igx-chip__item--selected')).toBe(false, "Chip should have been deselected"); + expect(chipItem.querySelector(QueryBuilderConstants.CHIP_SELECT_CLASS)).toBeNull(); + } + }; + + public static verifyExpressionChipSelection(fix, path: number[], shouldBeSelected: boolean) { + const chip = QueryBuilderFunctions.getQueryBuilderTreeExpressionChip(fix, path) as HTMLElement; + QueryBuilderFunctions.verifyChipSelectedState(chip, shouldBeSelected); + }; + + /** + * Verifies that all children (operator lines and expression chips) of the provided 'parent' are selected. + */ + public static verifyChildrenSelection(parent: HTMLElement, shouldBeSelected: boolean) { + const allOperatorLines: any[] = Array.from(parent.querySelectorAll('.igx-filter-tree__line')); + const allExpressionChips: any[] = Array.from(parent.querySelectorAll(`.igx-filter-tree__expression-item`)); + for (const operatorLine of allOperatorLines) { + if (operatorLine.checkVisibility()) { + QueryBuilderFunctions.verifyOperatorLineSelection(operatorLine, shouldBeSelected); + } else { + QueryBuilderFunctions.verifyOperatorLineSelection(operatorLine, false); + } + } + for (const expressionChip of allExpressionChips) { + if (expressionChip.checkVisibility()) { + QueryBuilderFunctions.verifyChipSelectedState(expressionChip, shouldBeSelected); + } else { + QueryBuilderFunctions.verifyChipSelectedState(expressionChip, false); + } + } + }; + + public static verifyQueryBuilderTabbableElements = (fixture: ComponentFixture) => { + const tabElements = QueryBuilderFunctions.getTabbableElements(fixture.nativeElement); + + let i = 0; + tabElements.forEach((element: HTMLElement) => { + switch (i) { + case 0: expect(element).toHaveClass('igx-filter-tree__line--and'); break; + case 1: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-input-group__input'); break; + case 3: expect(element).toHaveClass('igx-chip'); break; + case 4: expect(element).toHaveClass('igx-chip__remove'); break; + case 5: expect(element).toHaveClass('igx-chip'); break; + case 6: expect(element).toHaveClass('igx-chip__remove'); break; + case 7: expect(element).toHaveClass('igx-chip'); break; + case 8: expect(element).toHaveClass('igx-chip__remove'); break; + case 9: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('Condition'); break; + case 10: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('"And" Group'); break; + case 11: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('"Or" Group'); break; + } + i++; + }); + }; + + public static verifyTabbableChipActions = (chipActions: DebugElement) => { + const tabElements = QueryBuilderFunctions.getTabbableElements(chipActions.nativeElement); + + let i = 0; + tabElements.forEach((element: HTMLElement) => { + switch (i) { + case 0: expect(element).toHaveClass('igx-icon'); + expect(element.innerText).toContain('edit'); + break; + case 1: expect(element).toHaveClass('igx-icon'); + expect(element.innerText).toContain('add'); + break; + } + i++; + }); + }; + + public static verifyTabbableConditionEditLineElements = (editLine: DebugElement) => { + const tabElements = QueryBuilderFunctions.getTabbableElements(editLine.nativeElement); + + let i = 0; + tabElements.forEach((element: HTMLElement) => { + switch (i) { + case 0: expect(element).toHaveClass('igx-input-group__input'); break; + case 1: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-icon-button'); break; + case 3: expect(element).toHaveClass('igx-icon-button'); break; + } + i++; + }); + }; + + public static verifyTabbableInConditionDialogElements = (editDialog: DebugElement) => { + const tabElements = QueryBuilderFunctions.getTabbableElements(editDialog.nativeElement); + + let i = 0; + tabElements.forEach((element: HTMLElement) => { + switch (i) { + case 0: expect(element).toHaveClass('igx-filter-tree__line--and'); break; + case 1: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-input-group__input'); break; + case 3: expect(element).toHaveClass('igx-chip'); break; + case 4: expect(element).toHaveClass('igx-chip__remove'); break; + case 5: expect(element).toHaveClass('igx-chip'); break; + case 6: expect(element).toHaveClass('igx-chip__remove'); break; + case 7: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('Condition'); + break; + case 8: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('"And" Group'); + break; + case 9: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('"Or" Group'); + break; + } + i++; + }); + }; + + public static verifyExpressionChipContent(fix, path: number[], columnText: string, operatorText: string, valueText = undefined, level = 0) { + const chip = QueryBuilderFunctions.getQueryBuilderTreeExpressionChip(fix, path, level); + const chipSpans = Array.from(chip.querySelectorAll('span')); + const columnSpan = chipSpans[0]; + const operatorSpan = chipSpans[1]; + const valueSpan = chipSpans[2]; + expect(columnSpan.textContent.toLowerCase().trim()).toBe(columnText.toLowerCase(), 'incorrect chip column'); + expect(operatorSpan.textContent.toLowerCase().trim()).toBe(operatorText.toLowerCase(), 'incorrect chip operator'); + if (valueSpan != undefined && valueText != undefined) { + expect(valueSpan.textContent.toLowerCase().trim()).toBe(valueText.toLowerCase(), 'incorrect chip filter value'); + } + }; + + public static verifyGroupLineCount(fix: ComponentFixture, andLineCount: number = null, orLineCount: number = null) { + const andLines = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS}`)); + const orLines = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_OR_CSS_CLASS}`)); + + if (andLineCount) expect(andLines.length).toBe(andLineCount, "AND groups not the right count"); + if (orLineCount) expect(orLines.length).toBe(orLineCount, "OR groups not the right count"); + }; + + public static verifyRootAndSubGroupExpressionsCount(fix: ComponentFixture, rootDirect: number, rootTotal: number = null, subGroupPath: number[] = null, subGroupDirect: number = null, subGroupTotal: number = null) { + const rootGroup = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + expect(rootGroup).not.toBeNull('There is no root group.'); + expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(rootGroup, true).length).toBe(rootDirect, 'Root direct condition count not correct'); + expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(rootGroup, false).length).toBe(rootTotal, 'Root direct + child condition count not correct'); + if (subGroupPath) { + const subGroup = QueryBuilderFunctions.getQueryBuilderTreeItem(fix, subGroupPath) as HTMLElement; + if (subGroupDirect) expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(subGroup, true).length).toBe(subGroupDirect, 'Child direct condition count not correct'); + if (subGroupTotal) expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(subGroup, false).length).toBe(subGroupTotal, 'Child direct + child condition count not correct'); + } + }; + + public static selectEntityInEditModeExpression(fix: ComponentFixture, dropdownItemIndex: number, level = 0) { + QueryBuilderFunctions.clickQueryBuilderEntitySelect(fix, level); + fix.detectChanges(); + + const outlet = Array.from(fix.debugElement.nativeElement.querySelectorAll(`.igx-drop-down__list-scroll`)).filter(item => (item as HTMLElement).checkVisibility())[0]; + const item = Array.from((outlet as HTMLElement).querySelectorAll('.igx-drop-down__item'))[dropdownItemIndex] as HTMLElement; + UIInteractions.simulateClickAndSelectEvent(item) + tick(); + fix.detectChanges(); + } + + public static selectFieldsInEditModeExpression(fix, deselectItemIndexes, level = 0) { + QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fix, level); + fix.detectChanges(); + + const outlet = Array.from(fix.debugElement.nativeElement.querySelectorAll(`.igx-drop-down__list-scroll`)).filter(item => (item as HTMLElement).checkVisibility())[0]; + deselectItemIndexes.forEach(index => { + const item = Array.from((outlet as HTMLElement).querySelectorAll('.igx-drop-down__item'))[index] as HTMLElement; + UIInteractions.simulateClickAndSelectEvent(item) + tick(); + fix.detectChanges(); + }); + //close combo drop-down + QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fix); + fix.detectChanges(); + } + + public static selectColumnInEditModeExpression(fix, dropdownItemIndex: number, level = 0) { + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix, level); + fix.detectChanges(); + + const searchClass = `${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-${level}` + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${searchClass}`))[0].nativeElement; + QueryBuilderFunctions.clickQueryBuilderSelectDropdownItem(queryBuilderElement, dropdownItemIndex); + tick(); + fix.detectChanges(); + } + + public static selectOperatorInEditModeExpression(fix, dropdownItemIndex: number, level = 0) { + QueryBuilderFunctions.clickQueryBuilderOperatorSelect(fix, level); + fix.detectChanges(); + const searchClass = `${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-${level}` + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${searchClass}`))[0].nativeElement; + QueryBuilderFunctions.clickQueryBuilderSelectDropdownItem(queryBuilderElement, dropdownItemIndex); + tick(); + fix.detectChanges(); + } + + public static addChildGroup(fix: ComponentFixture, groupType: number, level: number) { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1, level); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0, level); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix, false, level).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + + //Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, level); + fix.detectChanges(); + } + + public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false, level); + // Enter values in the nested query + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false, level); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1, level); // Select 'ProductName' column. + + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, false, false, level); + + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0, level); // Select 'Contains' operator. + + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, true, false, level); + + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix, false, level).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + + // Verify all inputs + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, false, level - 1); // Parent commit button should be disabled + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, true, true, level); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Products', 'Id, ProductName, OrderId, Released', 'ProductName', 'Contains', 'a', level); + + //Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, level); + fix.detectChanges(); + } + + public static createGroupFromBottomTwoChips(fix: ComponentFixture, operator: string) { + //Select bottom two chips + const chips = fix.debugElement.queryAll(By.directive(IgxChipComponent)); + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chips[3].nativeElement, 200); + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chips[4].nativeElement, 200); + + //context menu should have opened + const contextMenus = QueryBuilderFunctions.getQueryBuilderContextMenus(fix); + expect(contextMenus.length).toBe(2); + + //Click 'create OR group' + const operatorButton = operator.toUpperCase() === "AND" ? 0 : + operator.toUpperCase() === "OR" ? 1 : null; + const orButton = contextMenus[1].queryAll(By.css('.igx-button'))[operatorButton]; + orButton.nativeElement.click(); + tick(); + fix.detectChanges(); + } +} diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts new file mode 100644 index 00000000000..9ba2fc86df9 --- /dev/null +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -0,0 +1,2121 @@ +import { waitForAsync, TestBed, ComponentFixture, fakeAsync, tick, flush } from '@angular/core/testing'; +import { FilteringExpressionsTree, FilteringLogic, IExpressionTree, IgxChipComponent, IgxDateFilteringOperand, IgxNumberFilteringOperand, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxQueryBuilderSearchValueTemplateDirective, IgxStringFilteringOperand } from 'igniteui-angular'; +import { configureTestSuite } from '../test-utils/configure-suite'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { By } from '@angular/platform-browser'; +import { ControlsFunction } from '../test-utils/controls-functions.spec'; +import { QueryBuilderFunctions, QueryBuilderConstants, SampleEntities } from './query-builder-functions'; +import { UIInteractions } from '../test-utils/ui-interactions.spec'; +import { FormsModule } from '@angular/forms'; + +describe('IgxQueryBuilder', () => { + configureTestSuite(); + let fix: ComponentFixture; + let queryBuilder: IgxQueryBuilderComponent; + beforeAll(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [ + NoopAnimationsModule, + IgxQueryBuilderComponent, + IgxQueryBuilderSampleTestComponent, + IgxQueryBuilderCustomTemplateSampleTestComponent, + ] + }).compileComponents(); + })); + + beforeEach(fakeAsync(() => { + fix = TestBed.createComponent(IgxQueryBuilderSampleTestComponent); + fix.detectChanges(); + queryBuilder = fix.componentInstance.queryBuilder; + })); + + describe('Basic', () => { + it('Should render empty Query Builder properly.', () => { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + expect(queryBuilderElement).toBeDefined(); + expect(queryBuilderElement.children.length).toEqual(2); + + expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fix)).toBe(' Query Builder '); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fix).textContent).toBe('and'); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fix).textContent).toBe('or'); + const queryTreeElement = queryBuilderElement.children[1]; + expect(queryTreeElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_TREE); + + expect(queryBuilder.expressionTree).toBeUndefined(); + + expect(queryTreeElement.children.length).toEqual(3); + const bodyElement = queryTreeElement.children[0]; + expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); + expect(bodyElement.children.length).toEqual(2); + expect(bodyElement.children[0]).toHaveClass('igx-query-builder__root'); + + const actionArea = bodyElement.children[0].querySelector('.igx-query-builder__root-actions'); + // initial add "'and'/'or' group " buttons should be displayed + expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); + // empty filtering tree message should be displayed + expect(bodyElement.children[0].children[1]).toHaveClass('igx-filter-empty'); + }); + + it('Should render Query Builder with initially set expression tree properly.', () => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; + const bodyElement = queryTreeElement.children[0]; + expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); + expect(bodyElement.children.length).toEqual(2); + // initial add "'and'/'or' group " buttons and empty filtering tree message should NOT be displayed + expect(bodyElement.querySelectorAll(':scope > button').length).toEqual(0); + expect(bodyElement.children[0]).toHaveClass('igx-filter-tree'); + + // Verify the operator line of the root group is an 'And' line. + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); + // all inputs should be displayed correctly + const queryTreeExpressionContainer = bodyElement.children[0].children[1]; + expect(queryTreeExpressionContainer).toHaveClass('igx-filter-tree__expression'); + expect(queryTreeExpressionContainer.children[0]).toHaveClass('igx-filter-tree__inputs'); + expect(queryTreeExpressionContainer.children[0].children[0].tagName).toEqual('IGX-SELECT'); + expect(queryTreeExpressionContainer.children[0].children[1].tagName).toEqual('IGX-COMBO'); + + const expressionItems = queryTreeExpressionContainer.querySelectorAll(':scope > .igx-filter-tree__expression-item'); + expect(expressionItems.length).toEqual(queryBuilder.expressionTree.filteringOperands.length); + // entity select should have proper value + expect(queryBuilder.queryTree.selectedEntity.name).toEqual(queryBuilder.expressionTree.entity); + // fields input should have proper value + expect(queryBuilder.queryTree.selectedReturnFields.length).toEqual(3); + // nested queries should be collapsed + const nestedQueryTrees = queryTreeExpressionContainer.querySelectorAll(QueryBuilderConstants.QUERY_BUILDER_TREE); + for (let i = 0; i < nestedQueryTrees.length; i++) { + expect(nestedQueryTrees[i].checkVisibility()).toBeFalse(); + } + // adding buttons should be enabled + const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const button of buttons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement, false); + } + }); + + it('Should render custom header properly.', () => { + const fixture = TestBed.createComponent(IgxQueryBuilderCustomTemplateSampleTestComponent); + fixture.detectChanges(); + + expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fixture)).toBe(' Custom Title '); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fixture)).toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fixture)).toBeNull(); + + fixture.componentInstance.showLegend = true; + fixture.detectChanges(); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fixture).textContent).toBe('and'); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fixture).textContent).toBe('or'); + }); + + it('Should render custom input template properly.', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxQueryBuilderCustomTemplateSampleTestComponent); + fixture.detectChanges(); + + //Select chip + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fixture, [0]); + tick(200); + fixture.detectChanges(); + + //Open edit mode + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fixture, [0], 'edit'); + tick(200); + fixture.detectChanges(); + + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fixture, false, 0); + const input = editModeContainer.querySelector('input.custom-class') as HTMLInputElement; + + expect(input).toBeDefined(); + expect(input.value).toBe('3'); + + //Edit input value + UIInteractions.clickAndSendInputElementValue(input, '5'); + tick(100); + fixture.detectChanges(); + + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fixture); + tick(100); + fixture.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fixture.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderId", + "condition": { + "name": "greaterThan", + "isUnary": false, + "iconName": "filter_greater_than" + }, + "conditionName": "greaterThan", + "searchVal": 5, + "searchTree": null, + "ignoreCase": true + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate" + ] +}`); + })); + }); + + describe('Interactions', () => { + it('Should correctly initialize a newly added \'And\' group.', fakeAsync(() => { + // Click the initial 'Add And Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Verify there is a new root group, which is empty. + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 0, 0); + + // Verify the operator line of the root group is an 'And' line. + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); + + // Verify the enabled/disabled state of each input of the expression in edit mode. + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + + // Verify the edit inputs are empty. + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', '', '', '', ''); + + // Verify adding buttons are disabled. + const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const button of buttons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement); + } + })); + + it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); + tick(100); + fix.detectChanges(); + + // Verify there is a new root group, which is empty. + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 0, 0); + + // Verify the operator line of the root group is an 'Or' line. + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'or'); + + // Verify the enabled/disabled state of each input of the expression in edit mode. + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + + // Verify the edit inputs are empty. + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', '', '', '', ''); + + // Verify adding buttons are disabled. + const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const button of buttons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement); + } + })); + + it(`Should discard newly added group when clicking on the 'cancel' button of its initial condition.`, fakeAsync(() => { + spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); + tick(100); + fix.detectChanges(); + + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(0); + + // Verify there is a new root group, which is empty. + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + expect(group).not.toBeNull('There is no root group.'); + + // Click on the 'cancel' button + const closeButton = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix); + UIInteractions.simulateClickEvent(closeButton); + tick(100); + fix.detectChanges(); + + // Verify there is a new root group, which is empty. + group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + expect(group).toBeNull(); + })); + + it('Should add a new condition to existing group by using add buttons.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + + // Verify group's children count before adding a new child. + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + + + // Add new 'expression'. + const buttonsContainer = Array.from(group.querySelectorAll('.igx-filter-tree__buttons'))[0]; + const buttons = Array.from(buttonsContainer.querySelectorAll('button')); + (buttons[0] as HTMLElement).click(); + tick(); + fix.detectChanges(); + + // Newly added condition should be empty + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, false, false, false); + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, '', '', ''); + + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'OrderName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 4, 7); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalled(); + })); + + it(`Should add a new 'And' group to existing group by using add buttons.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + + // Verify group's children count before adding a new child. + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + + // Add new 'And' group. + const buttonsContainer = Array.from(group.querySelectorAll('.igx-filter-tree__buttons'))[0]; + const buttons = Array.from(buttonsContainer.querySelectorAll('button')); + (buttons[1] as HTMLElement).click(); + tick(); + fix.detectChanges(); + + // Newly added condition should be empty + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, false, false, false); + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, '', '', ''); + + // adding buttons and 'end' group button should be disabled + let addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + expect(addingButtons.length).toBe(4); + for (const button of addingButtons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement); + } + + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'OrderName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + // Verify the operator line of the new group is an 'And' line. + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeGroupOperatorLine(fix, [0]) as HTMLElement, 'and'); + + // adding buttons should be enabled, 'end group' button should be disabled + addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + expect(addingButtons.length).toBe(4); + for (let i = 0; i < addingButtons.length; i++) { + if (i === 3) { + ControlsFunction.verifyButtonIsDisabled(addingButtons[i] as HTMLElement); + } else { + ControlsFunction.verifyButtonIsDisabled(addingButtons[i] as HTMLElement, false); + } + } + + // Start adding new condition to the currently added group + (addingButtons[0] as HTMLElement).click(); + tick(); + fix.detectChanges(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 4); // Select 'Today' operator. + + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + // expect all adding buttons and 'end group' button to be enabled + addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + expect(addingButtons.length).toBe(4); + for (let i = 0; i < addingButtons.length; i++) { + ControlsFunction.verifyButtonIsDisabled(addingButtons[i] as HTMLElement, false); + } + + // Click 'End Group' + (addingButtons[3] as HTMLElement).click(); + tick(); + fix.detectChanges(); + + group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 4, 9); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalled(); + })); + + it(`Should add a new 'Or' group to existing group by using add buttons.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + + // Verify group's children count before adding a new child. + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + + // Add new 'Or' group. + const buttonsContainer = Array.from(group.querySelectorAll('.igx-filter-tree__buttons'))[0]; + const buttons = Array.from(buttonsContainer.querySelectorAll('button')); + (buttons[2] as HTMLElement).click(); + tick(); + fix.detectChanges(); + + // Newly added condition should be empty + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, false, false, false); + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, '', '', ''); + + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'OrderName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + // Verify the operator line of the new group is an 'Or' line. + QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeGroupOperatorLine(fix, [0]) as HTMLElement, 'or'); + + group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 4, 8); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalled(); + })); + + it(`Should remove a condition from an existing group by using the 'close' icon of the respective chip.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + + // Verify tree layout before deleting chips. + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + + // Delete a chip and verify layout. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipRemoveIcon(fix, [0]); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 2, 2); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalled(); + + // Delete a chip and verify layout. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipRemoveIcon(fix, [1]); + tick(100); + flush(); + fix.detectChanges(); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 1, 1); + + // Verify remaining chip's content. + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0], 'OrderId', 'Greater Than', '3'); + + // Delete the last chip and verify that the group is deleted as well. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipRemoveIcon(fix, [0]); + tick(100); + flush(); + fix.detectChanges(); + + const rootGroup = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement;; + expect(rootGroup).toBeNull(); + })); + + it('Should discard the added group when clicking its operator line without having a single expression.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); + tick(100); + fix.detectChanges(); + + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + expect(group).not.toBeNull('There is no root group.'); + + // Click root group's operator line and verify that the root group and all of its children become selected. + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + + group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); + expect(group).toBeNull(); + })); + + it('Should be able to add and define a new group through initial adding button.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); + tick(100); + fix.detectChanges(); + + // Verify the enabled/disabled state of each input of the expression in edit mode. + expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + + // Select an entity + // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false); + // Select fields + QueryBuilderFunctions.selectFieldsInEditModeExpression(fix, [2, 3]) + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false); + + //Select Column + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, false, false); + + //Select Operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, true, false); + + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, true, true); + + // Verify all inputs values + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Products', 'Id, Released', 'ProductName', 'Contains', 'a'); + + //Commit the group + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "ProductName", + "condition": { + "name": "contains", + "isUnary": false, + "iconName": "filter_contains" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": "a", + "searchTree": null + } + ], + "operator": 1, + "entity": "Products", + "returnFields": [ + "Id", + "Released" + ] +}`); + })); + + it('Value input should be disabled for unary operator.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); + tick(100); + fix.detectChanges(); + + // Select an entity + // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + //Select Column + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, false, false); + + //Select Operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, true, false, true); + })); + + it('Fields dropdown should contain proper fields based on the entity.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select an entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + // Open fields dropdown and verify the items. + QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fix); + fix.detectChanges(); + + // TO DO: refactor when overlay issue is fixed + const outlet = fix.debugElement.queryAll(By.css(`.igx-drop-down__list-scroll`))[1].nativeElement; + const dropdownItems = Array.from(outlet.querySelectorAll('.igx-drop-down__item'));; + expect(dropdownItems.length).toBe(5); + expect((dropdownItems[0] as HTMLElement).innerText).toBe('Select All'); + expect((dropdownItems[1] as HTMLElement).innerText).toBe('Id'); + expect((dropdownItems[2] as HTMLElement).innerText).toBe('ProductName'); + expect((dropdownItems[3] as HTMLElement).innerText).toBe('OrderId'); + expect((dropdownItems[4] as HTMLElement).innerText).toBe('Released'); + })); + + it('Column dropdown should contain proper fields based on the entity.', fakeAsync(() => { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select an entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Open columns dropdown and verify the items. + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); + fix.detectChanges(); + const dropdownItems = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement); + expect(dropdownItems.length).toBe(3); + expect((dropdownItems[0] as HTMLElement).innerText).toBe('OrderId'); + expect((dropdownItems[1] as HTMLElement).innerText).toBe('OrderName'); + expect((dropdownItems[2] as HTMLElement).innerText).toBe('OrderDate'); + })); + + it('Operator dropdown should contain operators based on the column\'s datatype (\'string\' or \'number\' or \'date\').', fakeAsync(() => { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select an entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Select 'string' type column ('OrderName'). + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); + // Open the operator dropdown and verify they are 'string' specific. + QueryBuilderFunctions.clickQueryBuilderOperatorSelect(fix); + fix.detectChanges(); + let dropdownValues: string[] = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); + let expectedValues = ['Contains', 'Does Not Contain', 'Starts With', 'Ends With', 'Equals', + 'Does Not Equal', 'Empty', 'Not Empty', 'Null', 'Not Null', 'In', 'Not In']; + expect(dropdownValues).toEqual(expectedValues); + + // Close current dropdown by a random select. + QueryBuilderFunctions.clickQueryBuilderSelectDropdownItem(queryBuilderElement, 0); + tick(); + fix.detectChanges(); + + // Select 'number' type column ('OrderId'). + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); + // Open the operator dropdown and verify they are 'number' specific. + QueryBuilderFunctions.clickQueryBuilderOperatorSelect(fix); + fix.detectChanges(); + dropdownValues = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); + expectedValues = ['Equals', 'Does Not Equal', 'Greater Than', 'Less Than', 'Greater Than Or Equal To', + 'Less Than Or Equal To', 'Empty', 'Not Empty', 'Null', 'Not Null', 'In', 'Not In']; + expect(dropdownValues).toEqual(expectedValues); + + // Close current dropdown by a random select. + QueryBuilderFunctions.clickQueryBuilderSelectDropdownItem(queryBuilderElement, 0); + tick(); + fix.detectChanges(); + + // Select 'date' type column ('OrderDate'). + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); + // Open the operator dropdown and verify they are 'date' specific. + QueryBuilderFunctions.clickQueryBuilderOperatorSelect(fix); + fix.detectChanges(); + dropdownValues = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); + expectedValues = ['Equals', 'Does Not Equal', 'Before', 'After', 'Today', 'Yesterday', + 'This Month', 'Last Month', 'Next Month', 'This Year', 'Last Year', + 'Next Year', 'Empty', 'Not Empty', 'Null', 'Not Null', 'In', 'Not In']; + expect(dropdownValues).toEqual(expectedValues); + })); + + it('Operator dropdown should contain operators based on the column\'s datatype (\'boolean\').', fakeAsync(() => { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select an entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + // Select 'boolean' type column ('Released'). + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); + // Open the operator dropdown and verify they are 'boolean' specific. + QueryBuilderFunctions.clickQueryBuilderOperatorSelect(fix); + fix.detectChanges(); + const dropdownValues: string[] = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); + const expectedValues = ['All', 'True', 'False', 'Empty', 'Not Empty', 'Null', 'Not Null', 'In', 'Not In']; + expect(dropdownValues).toEqual(expectedValues); + })); + + it('Should correctly apply a \'string\' column condition through UI.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "ProductName", + "condition": { + "name": "startsWith", + "isUnary": false, + "iconName": "filter_starts_with" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": "a", + "searchTree": null + } + ], + "operator": 0, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] +}`); + })); + + it('Should correctly apply a \'Greater Than\' with \'number\' column condition through UI.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'Id' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, '5'); + tick(100); + fix.detectChanges(); + + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "Id", + "condition": { + "name": "greaterThan", + "isUnary": false, + "iconName": "filter_greater_than" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": 5, + "searchTree": null + } + ], + "operator": 0, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] +}`); + })); + + it('Should correctly apply a \'Equals\' with \'number\' column condition through UI.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'Id' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, '5'); + tick(100); + fix.detectChanges(); + + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "Id", + "condition": { + "name": "equals", + "isUnary": false, + "iconName": "filter_equal" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": 5, + "searchTree": null + } + ], + "operator": 0, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] +}`); + })); + + it('Should correctly apply a \'boolean\' column condition through UI.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); // Select 'Released' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); // Select 'True' operator. + + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "Released", + "condition": { + "name": "true", + "isUnary": true, + "iconName": "filter_true" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": null, + "searchTree": null + } + ], + "operator": 0, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] +}`); + })); + + it('Should correctly apply a \'date\' column condition through UI with unary operator.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 9); // Select 'This Year' operator. + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, true); // Third input should be disabled for unary operators. + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderDate", + "condition": { + "name": "thisYear", + "isUnary": true, + "iconName": "filter_this_year" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": null, + "searchTree": null + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate" + ] +}`); + })); + + it('Should correctly apply a \'date\' column condition through UI with value from calendar.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator. + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, true, false); + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix, true) as HTMLElement; + input.click(); + fix.detectChanges(); + + // Click on 'today' item in calendar. + const calendar = QueryBuilderFunctions.getQueryBuilderCalendar(fix); + const todayItem = calendar.querySelector('.igx-days-view__date--current'); + todayItem.firstChild.dispatchEvent(new Event('mousedown')); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, true, true); + + flush(); + })); + + it('Should correctly apply an \'in\' column condition through UI.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. + + // Verify operator icon + const operatorInputGroup = QueryBuilderFunctions.getQueryBuilderOperatorSelect(fix).querySelector('igx-input-group') as HTMLElement; + expect(operatorInputGroup.querySelector('igx-icon').attributes.getNamedItem('ng-reflect-name').nodeValue).toEqual('in'); + + // Verify inputs states + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, false); + + // Should render empty query builder tree + const nestedTree = fix.debugElement.query(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE)); + expect(nestedTree).toBeDefined(); + + QueryBuilderFunctions.addAndValidateChildGroup(fix, 1, 1); + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, true); // Parent commit button should be enabled + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderId", + "condition": { + "name": "in", + "isUnary": false, + "iconName": "in" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": null, + "searchTree": { + "filteringOperands": [ + { + "fieldName": "ProductName", + "condition": { + "name": "contains", + "isUnary": false, + "iconName": "filter_contains" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": "a", + "searchTree": null + } + ], + "operator": 1, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] + } + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate" + ] +}`); + })); + + it('Should correctly apply an \'not-in\' column condition through UI.', fakeAsync(() => { + // Verify there is no expression. + expect(queryBuilder.expressionTree).toBeUndefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 11); // Select 'Not-In' operator. + + // Verify operator icon + const operatorInputGroup = QueryBuilderFunctions.getQueryBuilderOperatorSelect(fix).querySelector('igx-input-group') as HTMLElement; + expect(operatorInputGroup.querySelector('igx-icon').attributes.getNamedItem('ng-reflect-name').nodeValue).toEqual('not-in'); + + // Verify inputs states + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, false); + + // Should render empty query builder tree + const nestedTree = fix.debugElement.query(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE)); + expect(nestedTree).toBeDefined(); + + QueryBuilderFunctions.addAndValidateChildGroup(fix, 1, 1); + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, true); // Parent commit button should be enabled + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderId", + "condition": { + "name": "notIn", + "isUnary": false, + "iconName": "not-in" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": null, + "searchTree": { + "filteringOperands": [ + { + "fieldName": "ProductName", + "condition": { + "name": "contains", + "isUnary": false, + "iconName": "filter_contains" + }, + "conditionName": null, + "ignoreCase": true, + "searchVal": "a", + "searchTree": null + } + ], + "operator": 1, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] + } + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate" + ] +}`); + })); + + it('Should correctly focus the search value input when editing the filtering expression', fakeAsync(() => { + //Create dateTime filtering expression + const tree = new FilteringExpressionsTree(FilteringLogic.And, null, 'Orders', ['OrderId']); + tree.filteringOperands.push({ + fieldName: 'OrderDate', + searchVal: new Date('2024-09-17T21:00:00.000Z'), + conditionName: 'equals', + condition: IgxDateFilteringOperand.instance().condition('equals') + }); + + queryBuilder.expressionTree = tree; + fix.detectChanges(); + + // Hover the last visible expression chip + const expressionItem = fix.nativeElement.querySelectorAll(`.${QueryBuilderConstants.QUERY_BUILDER_EXPRESSION_ITEM_CLASS}`)[0]; + expressionItem.dispatchEvent(new MouseEvent('mouseenter')); + tick(200); + fix.detectChanges(); + + // Click the edit icon to enter edit mode of the expression. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fix, [0], 'edit'); + tick(200); + fix.detectChanges(); + + //Check for the active element + const searchValueInput = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + expect(document.activeElement).toBe(searchValueInput, 'The input should be the active element.'); + })); + + it('Should select/deselect a condition when its respective chip is clicked.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Verify first chip is not selected. + QueryBuilderFunctions.verifyExpressionChipSelection(fix, [0], false); + + // Click first chip and verify it is selected. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyExpressionChipSelection(fix, [0], true); + // Verify actions container is visible. + expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) + .not.toBeNull('actions container is visible'); + + // Click first chip again and verify it is not selected. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyExpressionChipSelection(fix, [0], false); + // Verify actions container is not visible. + expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) + .toBeNull('actions container is visible'); + })); + + it('Should display edit and add buttons when hovering a chip.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + // Verify actions container is not visible. (This container contains the 'edit' and the 'add' buttons.) + expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) + .toBeNull('actions container is visible'); + + // Hover the first chip and verify actions container is visible. + UIInteractions.hoverElement(QueryBuilderFunctions.getQueryBuilderTreeItem(fix, [0]) as HTMLElement); + tick(50); + fix.detectChanges(); + expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) + .not.toBeNull('actions container is not visible'); + + // Unhover the first chip and verify actions container is not visible. + UIInteractions.unhoverElement(QueryBuilderFunctions.getQueryBuilderTreeItem(fix, [0]) as HTMLElement); + tick(50); + fix.detectChanges(); + expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) + .toBeNull('actions container is visible'); + })); + + it('Should have disabled adding buttons when an expression is in edit mode.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Verify adding buttons are enabled + let buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const button of buttons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement, false); + } + + // Enter edit mode + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true); + tick(50); + fix.detectChanges(); + + // Verify adding buttons are disabled + buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const button of buttons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement); + } + + // Exit edit mode + const closeButton = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix); + UIInteractions.simulateClickEvent(closeButton); + tick(100); + fix.detectChanges(); + + // Verify adding buttons are enabled + buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); + for (const button of buttons) { + ControlsFunction.verifyButtonIsDisabled(button as HTMLElement, false); + } + })); + + it('Double-clicking a condition should put it in edit mode.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Double-click the existing chip to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true); + tick(50); + fix.detectChanges(); + // Verify inputs values + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'OrderId', 'Greater Than', '3'); + // Edit the operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator. + // Commit the change + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + // Verify the chip + QueryBuilderFunctions.verifyExpressionChipContent(fix, [1], 'OrderId', 'Equals', '3'); + + // Verify that the nested query is not expanded + expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); + + // Double-click the nested query chip to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true); + tick(50); + fix.detectChanges(); + // Verify the query is expanded + expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeTrue(); + // Double-click a chip in the nested query three to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); + tick(50); + fix.detectChanges(); + // Verify inputs values + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'ProductName', 'Contains', 'a', 1); + // Edit the operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2, 1); // Select 'Starts With' operator. + // Commit the change + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, 1); + fix.detectChanges(); + // Verify the chip + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0], 'ProductName', 'Starts With', 'a', 1); + })); + + it('Should select/deselect all child conditions and groups when clicking a group\'s operator line.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + const childGroup = new FilteringExpressionsTree(FilteringLogic.Or, undefined, undefined); + childGroup.filteringOperands.push({ + fieldName: 'OrderNameName', + condition: IgxStringFilteringOperand.instance().condition('contains'), + conditionName: 'contains', + searchVal: 'a' + }); + queryBuilder.expressionTree.filteringOperands.push(childGroup); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + // Click root group's operator line and verify that the root group and all of its children become selected. + let rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyChildrenSelection(QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix) as HTMLElement, true); + + // Click root group's operator line again and verify that the root group and all of its children become unselected. + rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyChildrenSelection(QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fix) as HTMLElement, false); + + // Click an inner group's operator line and verify its children become selected. + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyChildrenSelection(QueryBuilderFunctions.getQueryBuilderTreeItem(fix, [0]) as HTMLElement, true); + + // Click an inner group's operator line again and verify its children become unselected. + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyChildrenSelection(QueryBuilderFunctions.getQueryBuilderTreeItem(fix, [0]) as HTMLElement, false); + })); + + it('Should display an alert dialog when the entity is changed.', fakeAsync(() => { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const queryTreeElement = queryBuilderElement.querySelector(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`); + const dialog = queryTreeElement.querySelector('igx-dialog'); + expect(dialog).toBeDefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + // Alert dialog should not be opened if there is no previous selection + expect(dialog.checkVisibility()).toBeFalse(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Alert dialog should be opened + expect(dialog.checkVisibility()).toBeTrue(); + })); + + it('Should reset all inputs when the entity is changed.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + //Select Column + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); + + //Select Operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, '1'); + tick(100); + fix.detectChanges(); + + // Verify all inputs values + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Products', 'Id, ProductName, OrderId, Released', 'Id', 'Equals', '1'); + + // Change the selected entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Confirm the change + const dialogOutlet: HTMLElement = fix.debugElement.queryAll(By.css(`.igx-dialog`))[0].nativeElement; + const confirmButton = Array.from(dialogOutlet.querySelectorAll('button'))[1]; + expect(confirmButton.innerText).toEqual('Confirm'); + confirmButton.click(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Verify all inputs + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate', '', '', ''); + })); + + it('Should NOT reset all inputs when the entity is not changed.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + //Select Column + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); + + //Select Operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, '1'); + tick(100); + fix.detectChanges(); + + // Verify all inputs values + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Products', 'Id, ProductName, OrderId, Released', 'Id', 'Equals', '1'); + + // Change the selected entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Decline the change + const dialogOutlet: HTMLElement = fix.debugElement.queryAll(By.css(`.igx-dialog`))[0].nativeElement; + const cancelButton = Array.from(dialogOutlet.querySelectorAll('button'))[0]; + expect(cancelButton.innerText).toEqual('Cancel'); + cancelButton.click(); + tick(100); + fix.detectChanges(); + + // Verify all inputs + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Products', 'Id, ProductName, OrderId, Released', 'Id', 'Equals', '1'); + })); + + it(`Should display 'expand'/'collapse' button properly.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; + // Nested query tree should have expand collapse button + const expressionItems = queryTreeElement.querySelectorAll('.igx-filter-tree__expression-item'); + expressionItems.forEach(item => { + const chip = item.querySelector('igx-chip'); + const conditionType = (chip.querySelector('.igx-filter-tree__expression-condition') as HTMLElement).innerText; + const toggleButton = item.querySelector('.igx-filter-tree__details-button'); + if (conditionType == 'In' || conditionType == 'Not In') { + expect(toggleButton).toBeDefined(); + } else { + expect(toggleButton).toBeNull(); + } + }); + })); + + it(`Clicking on parent "commit" button should apply all committed changes and discard all uncommitted changes in the child.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Double-click the parent chip 'Products' to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true); + tick(50); + fix.detectChanges(); + + // Double-click the child chip 'Released' to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true, 1); + tick(50); + fix.detectChanges(); + + // Change the 'Released' operator + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2, 1); // Select 'False' operator. + + // Verify both parent and child commit buttons are enabled + let parentCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix); + let childCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, 1); + + ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement, false); + ControlsFunction.verifyButtonIsDisabled(childCommitBtn as HTMLElement, false); + + // Commit the change + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, 1); + + // Double-click the child chip 'ProductName' to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); + tick(50); + fix.detectChanges(); + + // Change the 'ProductName' column to 'Id' + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0, 1); + + // Verify input values + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'Id', '', '', 1); + + // Verify parent commit button is enabled + parentCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix); + childCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, 1); + + ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement, false); + // Verify child commit button is disabled + ControlsFunction.verifyButtonIsDisabled(childCommitBtn as HTMLElement); + + // Commit the parent + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + tick(50); + fix.detectChanges(); + + // Verify expressions in the child query + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0], 'ProductName', 'Contains', 'a', 1); + QueryBuilderFunctions.verifyExpressionChipContent(fix, [1], 'Released', 'False', '', 1); + })); + + it('Should collapse nested query when it is committed.', fakeAsync(() => { + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. + + QueryBuilderFunctions.addChildGroup(fix, 0, 1); + + // Verify that the nested query is expanded + expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeTrue(); + + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + tick(100); + fix.detectChanges(); + + // Verify that the nested query is collapsed + expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); + })); + + it(`Should toggle the nested query on 'expand'/'collapse' button click.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); + + const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; + // Nested query tree should have expand collapse button + const expressionItems = Array.from(queryTreeElement.querySelectorAll('.igx-filter-tree__expression-item')); + const expandableItem = expressionItems.filter(item => + (item.querySelector('.igx-filter-tree__expression-condition') as HTMLElement).innerText == 'In' || + (item.querySelector('.igx-filter-tree__expression-condition') as HTMLElement).innerText == 'Not In' + )[0]; + const toggleBtn = expandableItem.querySelector('.igx-filter-tree__details-button') as HTMLElement; + expect((toggleBtn.querySelector('igx-icon') as HTMLElement).innerText).toBe('unfold_less'); + toggleBtn.click(); + tick(100); + fix.detectChanges(); + + expect((toggleBtn.querySelector('igx-icon') as HTMLElement).innerText).toBe('unfold_more'); + expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeTrue(); + })); + + it('Should create an "and"/"or" group on context menu button click and delete conditions on "delete filters" click.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + // Verify group types initially + QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); + + QueryBuilderFunctions.createGroupFromBottomTwoChips(fix, "OR") + + //OR group should have been created + QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 1); + let childGroup = QueryBuilderFunctions.getQueryBuilderTreeChildGroups(QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement); + expect(childGroup.length).toBe(1); + expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(childGroup[0] as HTMLElement).length).toBe(2); + + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0], 'OrderId', 'Greater Than', undefined); + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 1], 'OrderDate', 'After', undefined); + + QueryBuilderFunctions.createGroupFromBottomTwoChips(fix, "AND") + + //AND group should have been created + QueryBuilderFunctions.verifyGroupLineCount(fix, 3, 1); + + childGroup = QueryBuilderFunctions.getQueryBuilderTreeChildGroups(QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement); + expect(childGroup.length).toBe(1); + expect(QueryBuilderFunctions.getQueryBuilderTreeChildItems(childGroup[0] as HTMLElement).length).toBe(1); + + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0, 0], 'OrderId', 'Greater Than', undefined); + QueryBuilderFunctions.verifyExpressionChipContent(fix, [0, 0, 1], 'OrderDate', 'After', undefined); + + //Open Or group context menu + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0, 0]); + tick(); + fix.detectChanges(); + + //Click Delete group + const contextMenus = QueryBuilderFunctions.getQueryBuilderContextMenus(fix); + const deleteButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenus[1], 'Delete'); + deleteButton.nativeElement.click(); + tick(200); + fix.detectChanges(); + + //Group's conditions should have been deleted + const chips = fix.debugElement.queryAll(By.directive(IgxChipComponent)); + expect(chips.length).toBe(3, "Chips ware not deleted"); + })); + + it('Ungroup button of the root group\'s context menu should be disabled.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + // Click root group's operator line. + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + const contextMenu = QueryBuilderFunctions.getQueryBuilderContextMenus(fix)[1]; + + // Verify the unGroup button is disabled. + const unGroupButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'UnGroup'); + ControlsFunction.verifyButtonIsDisabled(unGroupButton.nativeElement); + })); + + it('Should remove a group from the expr tree when clicking "delete" from the context menu.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + + //create group + QueryBuilderFunctions.createGroupFromBottomTwoChips(fix, "AND"); + + //Click group line + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0]); + tick(200); + fix.detectChanges(); + + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + const contextMenu = QueryBuilderFunctions.getQueryBuilderContextMenus(fix)[1]; + + //click delete group + const deleteButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'Delete'); + deleteButton.nativeElement.click(); + tick(200); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 1, 4); + QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); + })); + + it('Should be able to open edit mode when condition is selected, close the edited condition on "close" button click and not commit it.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + //Select chip + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); + tick(200); + fix.detectChanges(); + + //Open edit mode + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fix, [1], 'edit'); + tick(); + fix.detectChanges(); + + const closeBtn = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix); + + // Verify the enabled/disabled state of each input of the expression in edit mode. + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, true, true); + + // Verify the edit inputs values. + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'OrderId', 'Greater Than', '3'); + + //edit condition fields + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'OrderName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + const value = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(value, '5'); + tick(200); + fix.detectChanges(); + + //cancel edit + closeBtn.click(); + tick(); + fix.detectChanges(); + + //Verify changes are reverted + QueryBuilderFunctions.verifyExpressionChipContent(fix, [1], 'OrderId', 'Greater Than', '3'); + })); + + it('Selecting/deselecting multiple conditions should display/hide the (create group)/(delete filters) context menu properly.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + + //select 1 + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + + //select 2 + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + //select 3 + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [2]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + // Unselecting conditions until one selected remains should hide the context menu + + //deselect 3 + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [2]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + //deselect 2 + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + })); + + it(`Should show/hide the group's context menu when clicking its operator line and should close the context menu when clicking its close button.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; + + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + + //Click line to open menu + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + //Click line to close menu + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + + //Click line to open menu + rootOperatorLine.click(); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + //Click close button to close menu + QueryBuilderFunctions.clickQueryBuilderContextMenuCloseButton(fix, 1); + tick(200); + fix.detectChanges(); + + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + })); + + it('Should be able to group, change And/Or and un-group conditions.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); + + //Select condition 1 and 2 + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); + tick(200); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + //Group them as AND group + const contextMenu = QueryBuilderFunctions.getQueryBuilderContextMenus(fix)[1]; + const andButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'Create "And" Group'); + andButton.nativeElement.click(); + tick(200); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 2, 7, [0], 2, 5); + QueryBuilderFunctions.verifyGroupLineCount(fix, 3, 0); + + //Change group to OR + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0]); + tick(200); + fix.detectChanges(); + const orButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'Or'); + orButton.nativeElement.click(); + tick(); + fix.detectChanges(); + tick(); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 2, 7, [0], 2, 5); + QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 1); + + //Un-group OR group + const unGroupButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'UnGroup'); + unGroupButton.nativeElement.click(); + tick(200); + fix.detectChanges(); + + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + })); + }); + + describe('Keyboard navigation', () => { + it('Should navigate with Tab/Shift+Tab through entity and fields inputs, chips, their respective delete icons and the operator lines.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + QueryBuilderFunctions.verifyQueryBuilderTabbableElements(fix); + })); + + it('Should navigate with Tab/Shift+Tab through chips" "edit", "cancel" and "adding" buttons, fields of a condition in edit mode.', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + const chip = fix.debugElement.queryAll(By.directive(IgxChipComponent))[0]; + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chip.nativeElement, 200); + + const chipActions = fix.debugElement.query(By.css('.igx-filter-tree__expression-actions')); + QueryBuilderFunctions.verifyTabbableChipActions(chipActions); + + // Open Edit mode and check condition line elements + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chipActions.children[0].nativeElement, 200); + + const editLine = fix.debugElement.queryAll(By.css('.igx-filter-tree__inputs'))[1]; + QueryBuilderFunctions.verifyTabbableConditionEditLineElements(editLine); + + const editDialog = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_BODY}`))[1]; + QueryBuilderFunctions.verifyTabbableInConditionDialogElements(editDialog); + })); + + it('Should select/deselect a condition when pressing \'Enter\' on its respective chip.', fakeAsync(() => { + //!Both Enter and Space should work + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + const chip = fix.debugElement.queryAll(By.directive(IgxChipComponent))[0]; + + QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, false); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chip.nativeElement, 200); + + QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, true); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chip.nativeElement, 200); + + QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, false); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, 'Enter', chip.nativeElement, 200); + + QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, true); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, 'Enter', chip.nativeElement, 200); + + QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, false); + })); + + //Should select/deselect all child conditions and groups when pressing \'Enter\' on a group\'s operator line. + //Should open the group"s context menu when pressing "Enter"/"space" on its operator line. + it('Should select/deselect all child conditions/groups and open/close group context menu when pressing "Enter"/"space" on its operator line.', fakeAsync(() => { + //!Both Enter and Space should work + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + const line = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS}`))[0]; + const chips = fix.debugElement.queryAll(By.directive(IgxChipComponent)); + + QueryBuilderFunctions.verifyChipSelectedState(chips[0].nativeElement, false); + QueryBuilderFunctions.verifyChipSelectedState(chips[3].nativeElement, false); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', line.nativeElement); + + QueryBuilderFunctions.verifyChipSelectedState(chips[0].nativeElement, true); + QueryBuilderFunctions.verifyChipSelectedState(chips[3].nativeElement, true); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', line.nativeElement); + + QueryBuilderFunctions.verifyChipSelectedState(chips[0].nativeElement, false); + QueryBuilderFunctions.verifyChipSelectedState(chips[3].nativeElement, false); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, 'Enter', line.nativeElement); + + QueryBuilderFunctions.verifyChipSelectedState(chips[0].nativeElement, true); + QueryBuilderFunctions.verifyChipSelectedState(chips[3].nativeElement, true); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, true); + + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, 'Enter', line.nativeElement); + + QueryBuilderFunctions.verifyChipSelectedState(chips[0].nativeElement, false); + QueryBuilderFunctions.verifyChipSelectedState(chips[3].nativeElement, false); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); + })); + + it('Should remove a chip in when pressing \'Enter\' on its \'remove\' icon.', fakeAsync(() => { + //!Both Enter and Space should work + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + // Verify the there are three chip expressions. + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); + + // Press 'Enter' on the remove icon of the second chip. + const chip = QueryBuilderFunctions.getQueryBuilderTreeExpressionChip(fix, [1]); + const removeIcon = ControlsFunction.getChipRemoveButton(chip as HTMLElement); + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, 'Enter', removeIcon); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 2, 5); + + // Press 'Space' on the remove icon of the second chip. + const chip2 = QueryBuilderFunctions.getQueryBuilderTreeExpressionChip(fix, [0]); + const removeIcon2 = ControlsFunction.getChipRemoveButton(chip2 as HTMLElement); + QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', removeIcon2); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 1, 1); + })); + }); + + describe('Localization', () => { + it('Should correctly change resource strings for Query Builder.', fakeAsync(() => { + queryBuilder.resourceStrings = Object.assign({}, queryBuilder.resourceStrings, { + igx_query_builder_title: 'My advanced filter', + igx_query_builder_and_group: 'My and group', + igx_query_builder_or_group: 'My or group', + igx_query_builder_end_group: 'My end group', + igx_query_builder_create_and_group: 'My create and group', + igx_query_builder_create_or_group: 'My create or group', + igx_query_builder_and_label: 'My and', + igx_query_builder_or_label: 'My or', + igx_query_builder_add_condition: 'My condition', + igx_query_builder_ungroup: 'My ungroup', + igx_query_builder_delete: 'My delete', + igx_query_builder_delete_filters: 'My delete filters', + igx_query_builder_initial_text: 'My initial text' + }); + fix.detectChanges(); + + expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fix)).toBe(' My advanced filter '); + expect((QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fix) as HTMLElement).innerText).toBe('My and'); + expect((QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fix) as HTMLElement).innerText).toBe('My or'); + expect(QueryBuilderFunctions.getQueryBuilderInitialAddGroupButtons(fix)[0].querySelector('span').innerText) + .toBe('My and group'); + expect(QueryBuilderFunctions.getQueryBuilderInitialAddGroupButtons(fix)[1].querySelector('span').innerText) + .toBe('My or group'); + expect((QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix) as HTMLElement).innerText).toBe('My initial text'); + + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement).querySelector('span').innerText) + .toBe('My condition'); + expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[1] as HTMLElement).querySelector('span').innerText) + .toBe('My and group'); + expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[2] as HTMLElement).querySelector('span').innerText) + .toBe('My or group'); + + // Populate edit inputs. + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products'. + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + + let input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; + rootOperatorLine.click(); + fix.detectChanges(); + + const buttonGroupItems = Array.from(QueryBuilderFunctions.getQueryBuilderContextMenuButtonGroup(fix).querySelectorAll('.igx-button-group__item-content')); + expect((buttonGroupItems[0] as HTMLElement).textContent).toBe('My and'); + expect((buttonGroupItems[1] as HTMLElement).textContent).toBe('My or'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[3] as HTMLElement).querySelector('span').innerText) + .toBe('My ungroup'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[4] as HTMLElement).querySelector('span').innerText) + .toBe('My delete'); + + // Close context menu. + QueryBuilderFunctions.clickQueryBuilderContextMenuCloseButton(fix); + fix.detectChanges(); + + // Add another expression to root group. + let btn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement; + btn.click(); + fix.detectChanges(); + + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. + + input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, 'a'); + tick(100); + fix.detectChanges(); + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + // Select two chips. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0]); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); + tick(200); + fix.detectChanges(); + + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[1] as HTMLElement).innerText).toBe('My create and group'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[2] as HTMLElement).innerText).toBe('My create or group'); + expect((QueryBuilderFunctions.getQueryBuilderContextMenuButtons(fix)[3] as HTMLElement).innerText).toBe('My delete filters'); + + // Close context menu. + QueryBuilderFunctions.clickQueryBuilderContextMenuCloseButton(fix); + tick(100); + fix.detectChanges(); + + // Add an 'or' group to root group. + btn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[2] as HTMLElement; + btn.click(); + tick(100); + fix.detectChanges(); + + const endGroupButton = QueryBuilderFunctions.getQueryBuilderTreeGroupButtons(fix, [0], 0)[3] as HTMLElement; + expect(endGroupButton.querySelector('span').innerText).toBe('My end group'); + })); + }); + + describe('Overlay settings', () => { + it('', () => { }); + }); +}); + +@Component({ + template: ` + + + `, + standalone: true, + imports: [ + IgxQueryBuilderComponent + ] +}) +export class IgxQueryBuilderSampleTestComponent implements OnInit { + @ViewChild(IgxQueryBuilderComponent) public queryBuilder: IgxQueryBuilderComponent; + public entities: Array; + + public ngOnInit(): void { + this.entities = SampleEntities.map(a => ({ ...a })); + } +} + +@Component({ + template: ` + + + + + + + `, + standalone: true, + imports: [ + IgxQueryBuilderComponent, + IgxQueryBuilderHeaderComponent, + IgxQueryBuilderSearchValueTemplateDirective, + FormsModule + ] +}) +export class IgxQueryBuilderCustomTemplateSampleTestComponent implements OnInit { + @ViewChild(IgxQueryBuilderComponent) public queryBuilder: IgxQueryBuilderComponent; + @ViewChild('searchValueTemplate', { read: IgxQueryBuilderSearchValueTemplateDirective, static: true }) + public searchValueTemplate: IgxQueryBuilderSearchValueTemplateDirective; + public entities: Array; + public showLegend = false; + public expressionTree: IExpressionTree; + + + public ngOnInit(): void { + this.entities = SampleEntities.map(a => ({ ...a })); + + const tree = new FilteringExpressionsTree(FilteringLogic.And, null, 'Orders', ['*']); + tree.filteringOperands.push({ + fieldName: 'OrderId', + condition: IgxNumberFilteringOperand.instance().condition('greaterThan'), + conditionName: 'greaterThan', + searchVal: 3, + ignoreCase: true + }); + + this.expressionTree = tree; + } +} diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts index 5df2f464cbd..674d80666e7 100644 --- a/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/test-utils/grid-functions.spec.ts @@ -702,7 +702,7 @@ export class GridFunctions { /** * Gets the ESF tree node icon */ - public static getExcelFilterTreeNodeIcon(fix: ComponentFixture, index: number) { + public static getExcelFilterTreeNodeIcon(fix: ComponentFixture, index: number) { const treeNodeEl = fix.debugElement.queryAll(By.directive(IgxTreeNodeComponent))[index]?.nativeElement; const expandIcon = treeNodeEl.querySelector(TREE_NODE_TOGGLE); return expandIcon; @@ -1317,110 +1317,6 @@ export class GridFunctions { applyButton.click(); } - /** - * (Double)Click the underlying chip of the expression that is located on the provided 'path'. - */ - public static clickAdvancedFilteringTreeExpressionChip(fix: ComponentFixture, path: number[], dblClick = false) { - const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, path); - if (dblClick) { - chip.dispatchEvent(new MouseEvent('dblclick')); - } else { - chip.click(); - } - } - - /** - * Click the remove icon of the expression that is located on the provided 'path'. - */ - public static clickAdvancedFilteringTreeExpressionChipRemoveIcon(fix: ComponentFixture, path: number[]) { - const chip = GridFunctions.getAdvancedFilteringTreeExpressionChip(fix, path); - ControlsFunction.clickChipRemoveButton(chip); - } - - /** - * Click the edit icon of the expression that is located on the provided 'path'. - */ - public static clickAdvancedFilteringTreeExpressionChipEditIcon(fix: ComponentFixture, path: number[]) { - const chipEditIcon = GridFunctions.getAdvancedFilteringTreeExpressionEditIcon(fix, path); - chipEditIcon.click(); - } - - /** - * Click the add icon of the expression that is located on the provided 'path'. - */ - public static clickAdvancedFilteringTreeExpressionChipAddIcon(fix: ComponentFixture, path: number[]) { - const chipAddIcon = GridFunctions.getAdvancedFilteringTreeExpressionAddIcon(fix, path); - chipAddIcon.click(); - } - - /** - * Click the operator line of the group that is located on the provided 'path'. - */ - public static clickAdvancedFilteringTreeGroupOperatorLine(fix: ComponentFixture, path: number[]) { - const operatorLine = GridFunctions.getAdvancedFilteringTreeGroupOperatorLine(fix, path); - operatorLine.click(); - } - - /** - * Click the column select for the expression that is currently in edit mode. - */ - public static clickAdvancedFilteringColumnSelect(fix: ComponentFixture) { - const columnSelect = GridFunctions.getAdvancedFilteringColumnSelect(fix); - const inputGroup = columnSelect.querySelector('igx-input-group'); - inputGroup.click(); - } - - /** - * Click the operator select for the expression that is currently in edit mode. - */ - public static clickAdvancedFilteringOperatorSelect(fix: ComponentFixture) { - const operatorSelect = GridFunctions.getAdvancedFilteringOperatorSelect(fix); - const inputGroup = operatorSelect.querySelector('igx-input-group'); - inputGroup.click(); - } - - /** - * Click the value input for the expression that is currently in edit mode. - * (NOTE: The value input could be either an input group or a date picker.) - */ - public static clickAdvancedFilteringValueInput(fix: ComponentFixture, dateType = false) { - // Could be either an input group or a date picker. - const valueInput = GridFunctions.getAdvancedFilteringValueInput(fix, dateType); - valueInput.click(); - } - - /** - * Click the the select dropdown's element that is positioned at the specified 'index'. - * (NOTE: This method presumes that the select dropdown is already opened.) - */ - public static clickAdvancedFilteringSelectDropdownItem(fix: ComponentFixture, index: number) { - const selectDropdownItems = GridFunctions.sortNativeElementsVertically( - Array.from(GridFunctions.getAdvancedFilteringSelectDropdownItems(fix))); - const item = selectDropdownItems[index]; - item.click(); - } - - /** - * Click the commit button of the expression that is currently in edit mode. - */ - public static clickAdvancedFilteringExpressionCommitButton(fix: ComponentFixture) { - const commitButton = GridFunctions.getAdvancedFilteringExpressionCommitButton(fix); - commitButton.click(); - } - - /** - * Click the close button of the expression that is currently in edit mode. - */ - public static clickAdvancedFilteringExpressionCloseButton(fix: ComponentFixture) { - const closeButton = GridFunctions.getAdvancedFilteringExpressionCloseButton(fix); - closeButton.click(); - } - - public static clickAdvancedFilteringContextMenuCloseButton(fix: ComponentFixture) { - const contextMenuCloseButton = GridFunctions.getAdvancedFilteringContextMenuCloseButton(fix); - contextMenuCloseButton.click(); - } - public static getAdvancedFilteringComponent(fix: ComponentFixture) { const gridNativeElement = fix.debugElement.query(By.css('igx-grid')).nativeElement; let advFilterDialog = gridNativeElement.querySelector('.igx-advanced-filter'); @@ -1431,236 +1327,6 @@ export class GridFunctions { return advFilterDialog; } - public static getAdvancedFilteringEmptyPrompt(fix: ComponentFixture) { - const advFilterDialog = GridFunctions.getAdvancedFilteringComponent(fix); - const emptyPrompt = advFilterDialog.querySelector('.igx-filter-empty'); - return emptyPrompt; - } - - public static getAdvancedFilteringHeader(fix: ComponentFixture) { - const advFilterDialog = GridFunctions.getAdvancedFilteringComponent(fix); - const header = advFilterDialog.querySelector('.igx-query-builder__header'); - return header; - } - - public static getAdvancedFilteringHeaderText(fix: ComponentFixture) { - const header = GridFunctions.getAdvancedFilteringHeader(fix); - const title = header.querySelector('.ig-typography__h6'); - return title.innerText; - } - - public static getAdvancedFilteringHeaderLegendItemAnd(fix: ComponentFixture) { - const header = GridFunctions.getAdvancedFilteringHeader(fix); - const andLegendItem = header.querySelector('.igx-builder-legend__item--and'); - return andLegendItem; - } - - public static getAdvancedFilteringHeaderLegendItemOr(fix: ComponentFixture) { - const header = GridFunctions.getAdvancedFilteringHeader(fix); - const orLegendItem = header.querySelector('.igx-builder-legend__item--or'); - return orLegendItem; - } - - public static getAdvancedFilteringAllGroups(fix: ComponentFixture): any[] { - const advFilterDialog = GridFunctions.getAdvancedFilteringComponent(fix); - const allGroups = Array.from(GridFunctions.getAdvancedFilteringTreeChildGroups(advFilterDialog, false)); - return allGroups; - } - - /** - * Get the expressions container that contains all groups and expressions. - */ - public static getAdvancedFilteringExpressionsContainer(fix: ComponentFixture) { - const advFilterDialog = GridFunctions.getAdvancedFilteringComponent(fix); - const exprContainer = advFilterDialog.querySelector('.igx-query-builder__main'); - return exprContainer; - } - - /** - * Get the root group. - */ - public static getAdvancedFilteringTreeRootGroup(fix: ComponentFixture) { - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - const rootGroup = exprContainer.querySelector(':scope > .igx-filter-tree'); - return rootGroup; - } - - /** - * Get all child groups of the given 'group' by specifying whether to include its direct child groups only - * or all of its child groups in the hierarchy. (NOTE: Expressions do not have children!) - */ - public static getAdvancedFilteringTreeChildGroups(group: HTMLElement, directChildrenOnly = true) { - const pattern = directChildrenOnly ? ':scope > .igx-filter-tree' : '.igx-filter-tree'; - const childrenContainer = group.querySelector('.igx-filter-tree__expression'); - const childGroups = GridFunctions.sortNativeElementsVertically(Array.from(childrenContainer.querySelectorAll(pattern))); - return childGroups; - } - - /** - * Get all child expressions of the given 'group' by specifying whether to include its direct child expressions only - * or all of its child expressions in the hierarchy. - */ - public static getAdvancedFilteringTreeChildExpressions(group: HTMLElement, directChildrenOnly = true) { - const pattern = directChildrenOnly ? ':scope > .igx-filter-tree__expression-item' : '.igx-filter-tree__expression-item'; - const childrenContainer = group.querySelector('.igx-filter-tree__expression'); - const childExpressions = GridFunctions.sortNativeElementsVertically(Array.from(childrenContainer.querySelectorAll(pattern))); - return childExpressions; - } - - /** - * Get all child groups and expressions of the given 'group' by specifying whether to include its - * direct child groups and expressions only or all of its child groups and expressions in the hierarchy. - */ - public static getAdvancedFilteringTreeChildItems(group: HTMLElement, directChildrenOnly = true) { - const childGroups = Array.from(GridFunctions.getAdvancedFilteringTreeChildGroups(group, directChildrenOnly)); - const childExpressions = Array.from(GridFunctions.getAdvancedFilteringTreeChildExpressions(group, directChildrenOnly)); - return GridFunctions.sortNativeElementsVertically(childGroups.concat(childExpressions)); - } - - /** - * Get a specific item from the tree (could be a group or an expression) - * by specifying its hierarchical path (not including the root group). - * (Example: [2 ,1] will first get the third item of the root group, - * and then it will get the second item of the root group's third item.) - * (NOTE: Only the items that are groups have children.) - * The returned element is the one that has been gotten last. - */ - public static getAdvancedFilteringTreeItem(fix: ComponentFixture, - path: number[]) { - let node = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - for (const pos of path) { - const directChildren = GridFunctions.getAdvancedFilteringTreeChildItems(node, true); - node = directChildren[pos]; - } - return node; - } - - /** - * Get the operator line of the root group. - */ - public static getAdvancedFilteringTreeRootGroupOperatorLine(fix: ComponentFixture) { - const rootGroup = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - const directOperatorLine = rootGroup.querySelector(':scope > .igx-filter-tree__line'); - return directOperatorLine; - } - - /** - * Get the operator line of the group that is located on the provided 'path'. - */ - public static getAdvancedFilteringTreeGroupOperatorLine(fix: ComponentFixture, path: number[]) { - const group = GridFunctions.getAdvancedFilteringTreeItem(fix, path); - const directOperatorLine = group.querySelector(':scope > .igx-filter-tree__line'); - return directOperatorLine; - } - - /** - * Get the underlying chip of the expression that is located on the provided 'path'. - */ - public static getAdvancedFilteringTreeExpressionChip(fix: ComponentFixture, path: number[]) { - const treeItem = GridFunctions.getAdvancedFilteringTreeItem(fix, path); - const chip = treeItem.querySelector('igx-chip'); - return chip; - } - - /** - * Get the action icons ('edit' and 'add') of the expression that is located on the provided 'path'. - */ - public static getAdvancedFilteringTreeExpressionActionsContainer(fix: ComponentFixture, path: number[]) { - const treeItem = GridFunctions.getAdvancedFilteringTreeItem(fix, path); - const actionsContainer = treeItem.querySelector('.igx-filter-tree__expression-actions'); - return actionsContainer; - } - - /** - * Get the edit icon of the expression that is located on the provided 'path'. - */ - public static getAdvancedFilteringTreeExpressionEditIcon(fix: ComponentFixture, path: number[]) { - const actionsContainer = GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, path); - const icons = Array.from(actionsContainer.querySelectorAll('igx-icon')); - const editIcon: any = icons.find((icon: any) => icon.innerText === 'edit'); - return editIcon; - } - - /** - * Get the add icon of the expression that is located on the provided 'path'. - */ - public static getAdvancedFilteringTreeExpressionAddIcon(fix: ComponentFixture, path: number[]) { - const actionsContainer = GridFunctions.getAdvancedFilteringTreeExpressionActionsContainer(fix, path); - const icons = Array.from(actionsContainer.querySelectorAll('igx-icon')); - const addIcon: any = icons.find((icon: any) => icon.innerText === 'add'); - return addIcon; - } - - /** - * Get the adding buttons and the cancel button of a group by specifying the - * path of the group and the index position of the buttons container. - * (NOTE: The buttons are returned in an array and are sorted in ascending order based on 'X' value.) - */ - public static getAdvancedFilteringTreeGroupButtons(fix: ComponentFixture, path: number[], buttonsIndex: number) { - const group = GridFunctions.getAdvancedFilteringTreeItem(fix, path); - const childrenContainer = group.querySelector('.igx-filter-tree__expression'); - const buttonsContainers = GridFunctions.sortNativeElementsVertically( - Array.from(childrenContainer.querySelectorAll(':scope > .igx-filter-tree__buttons'))); - const buttonsContainer: any = buttonsContainers[buttonsIndex]; - const buttons = GridFunctions.sortNativeElementsHorizontally(Array.from(buttonsContainer.querySelectorAll('button'))); - return buttons; - } - - /** - * Get the adding buttons and the cancel button of the root group by specifying the - * index position of the buttons container. - * (NOTE: The buttons are returned in an array and are sorted in ascending order based on 'X' value.) - */ - public static getAdvancedFilteringTreeRootGroupButtons(fix: ComponentFixture, buttonsIndex: number) { - const group = GridFunctions.getAdvancedFilteringTreeRootGroup(fix); - const childrenContainer = group.querySelector('.igx-filter-tree__expression'); - const buttonsContainers = GridFunctions.sortNativeElementsVertically( - Array.from(childrenContainer.querySelectorAll(':scope > .igx-filter-tree__buttons'))); - const buttonsContainer: any = buttonsContainers[buttonsIndex]; - const buttons = GridFunctions.sortNativeElementsHorizontally(Array.from(buttonsContainer.querySelectorAll('button'))); - return buttons; - } - - /** - * Get the initial group adding buttons when the dialog does not contain any filters. - */ - public static getAdvancedFilteringInitialAddGroupButtons(fix: ComponentFixture) { - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - const initialButtons = GridFunctions.sortNativeElementsHorizontally( - Array.from(exprContainer.querySelectorAll(':scope > button'))); - return initialButtons; - } - - public static clickAdvancedFilteringInitialAddGroupButton(fix: ComponentFixture, buttonIndex: number) { - const addAndGroupButton = this.getAdvancedFilteringInitialAddGroupButtons(fix)[buttonIndex]; - addAndGroupButton.click(); - } - - public static getAdvancedFilteringContextMenu(fix: ComponentFixture) { - const gridNativeElement = fix.debugElement.query(By.css('igx-grid')).nativeElement; - const contextMenu = gridNativeElement.querySelector('.igx-filter-contextual-menu'); - return contextMenu; - } - - public static getAdvancedFilteringContextMenuButtons(fix: ComponentFixture) { - const contextMenu = GridFunctions.getAdvancedFilteringContextMenu(fix); - const buttons = GridFunctions.sortNativeElementsVertically(Array.from(contextMenu.querySelectorAll('button'))); - return buttons; - } - - public static getAdvancedFilteringContextMenuCloseButton(fix: ComponentFixture) { - const contextMenu = GridFunctions.getAdvancedFilteringContextMenu(fix); - const buttons = GridFunctions.sortNativeElementsVertically(Array.from(contextMenu.querySelectorAll('button'))); - const closeButton: any = buttons.find((b: any) => b.innerText.toLowerCase() === 'close'); - return closeButton; - } - - public static getAdvancedFilteringContextMenuButtonGroup(fix: ComponentFixture) { - const contextMenu = GridFunctions.getAdvancedFilteringContextMenu(fix); - const buttonGroup = contextMenu.querySelector('igx-buttongroup'); - return buttonGroup; - } - public static getAdvancedFilteringFooter(fix: ComponentFixture) { const advFilterDialog = GridFunctions.getAdvancedFilteringComponent(fix); const footer = advFilterDialog.querySelector('.igx-excel-filter__secondary-footer'); @@ -1688,54 +1354,6 @@ export class GridFunctions { return applyFilterButton; } - public static getAdvancedFilteringEditModeContainer(fix: ComponentFixture) { - const exprContainer = GridFunctions.getAdvancedFilteringExpressionsContainer(fix); - const editModeContainer = exprContainer.querySelector('.igx-filter-tree__inputs'); - return editModeContainer; - } - - public static getAdvancedFilteringColumnSelect(fix: ComponentFixture) { - const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix); - const selects = GridFunctions.sortNativeElementsHorizontally(Array.from(editModeContainer.querySelectorAll('igx-select'))); - const columnSelect = selects[0]; - return columnSelect; - } - - public static getAdvancedFilteringOperatorSelect(fix: ComponentFixture) { - const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix); - const selects = GridFunctions.sortNativeElementsHorizontally(Array.from(editModeContainer.querySelectorAll('igx-select'))); - const columnSelect = selects[1]; - return columnSelect; - } - - public static getAdvancedFilteringValueInput(fix: ComponentFixture, dateType = false) { - const editModeContainer = GridFunctions.getAdvancedFilteringEditModeContainer(fix); - const input = dateType ? - editModeContainer.querySelector('igx-date-picker').querySelector('input') : - GridFunctions.sortNativeElementsHorizontally(Array.from(editModeContainer.querySelectorAll('igx-input-group')))[2]; - return input; - } - - public static getAdvancedFilteringExpressionCommitButton(fix: ComponentFixture) { - const actionButtons = fix.debugElement.queryAll(By.css('.igx-filter-tree__inputs-actions > button')); - const commitButton = actionButtons.find((el: DebugElement) => { - const icon = el.query(By.directive(IgxIconComponent)).componentInstance; - return icon.name === 'confirm'; - }).nativeElement; - - return commitButton; - } - - public static getAdvancedFilteringExpressionCloseButton(fix: ComponentFixture) { - const actionButtons = fix.debugElement.queryAll(By.css('.igx-filter-tree__inputs-actions > button')); - const closeButton = actionButtons.find((el: DebugElement) => { - const icon = el.query(By.directive(IgxIconComponent)).componentInstance; - return icon.name === 'close'; - }).nativeElement; - - return closeButton; - } - public static getAdvancedFilteringOutlet(fix: ComponentFixture) { const gridNativeElement = fix.debugElement.query(By.css('igx-grid')).nativeElement; let advFilteringDialog = gridNativeElement.querySelector('igx-advanced-filtering-dialog'); @@ -1747,26 +1365,6 @@ export class GridFunctions { return outlet; } - public static getAdvancedFilteringSelectDropdown(fix: ComponentFixture) { - const outlet = GridFunctions.getAdvancedFilteringOutlet(fix); - const selectDropdown = outlet.querySelector('.igx-drop-down__list-scroll'); - return selectDropdown; - } - - public static getAdvancedFilteringSelectDropdownItems(fix: ComponentFixture) { - const selectDropdown = GridFunctions.getAdvancedFilteringSelectDropdown(fix); - const items = GridFunctions.sortNativeElementsVertically( - Array.from(selectDropdown.querySelectorAll('.igx-drop-down__item'))); - return items; - } - - public static getAdvancedFilteringCalendar(fix: ComponentFixture) { - const gridNativeElement = fix.debugElement.query(By.css('igx-grid')).nativeElement; - const calendar = gridNativeElement.querySelector('.igx-calendar'); - return calendar; - } - - public static setOperatorESF(fix: ComponentFixture, expressionIndex: number, itemIndex: number) { const input: HTMLInputElement = GridFunctions.getExcelFilteringDDInput(fix, expressionIndex); input.click(); From 37ad702370f6e357ad11de0cd2ea7b9bc688208f Mon Sep 17 00:00:00 2001 From: teodosia Date: Tue, 24 Sep 2024 15:16:32 +0300 Subject: [PATCH 069/147] feat(query-builder): fix parent commit/cancel edit mode behavior --- .../query-builder-tree.component.ts | 6 +++- .../query-builder.component.spec.ts | 28 +++++++++++-------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 028e6d3f7c6..bd7df219be4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -775,6 +775,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; if (innerQuery) { innerQuery.expressionTree = this._initialExpressionTree; + + if (innerQuery._editedExpression) { + innerQuery.cancelOperandEdit(); + } } } @@ -797,7 +801,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { return this.selectedField && this.selectedCondition && ( ((!!this.searchValue || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || - (innerQuery && !!innerQuery.expressionTree) || + (innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined) || this.selectedField.filters.condition(this.selectedCondition).isUnary ); } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index 9ba2fc86df9..154e0e206e4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -1138,7 +1138,7 @@ describe('IgxQueryBuilder', () => { tree.filteringOperands.push({ fieldName: 'OrderDate', searchVal: new Date('2024-09-17T21:00:00.000Z'), - conditionName: 'equals', + conditionName: 'equals', condition: IgxDateFilteringOperand.instance().condition('equals') }); @@ -1467,7 +1467,7 @@ describe('IgxQueryBuilder', () => { }); })); - it(`Clicking on parent "commit" button should apply all committed changes and discard all uncommitted changes in the child.`, fakeAsync(() => { + it(`Parent "commit" button should be disabled if a child condition is edited.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); tick(100); @@ -1490,7 +1490,7 @@ describe('IgxQueryBuilder', () => { let parentCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix); let childCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, 1); - ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement, false); + ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement); ControlsFunction.verifyButtonIsDisabled(childCommitBtn as HTMLElement, false); // Commit the change @@ -1507,22 +1507,28 @@ describe('IgxQueryBuilder', () => { // Verify input values QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'Id', '', '', 1); - // Verify parent commit button is enabled + // Verify parent and child commit buttons are disabled parentCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix); childCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, 1); - ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement, false); - // Verify child commit button is disabled + ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement); ControlsFunction.verifyButtonIsDisabled(childCommitBtn as HTMLElement); - // Commit the parent - QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0, 1); + //Type Value + const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix, false, 1).querySelector('input'); + UIInteractions.clickAndSendInputElementValue(input, '1'); + tick(100); + fix.detectChanges(); + + // Commit the child + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, 1); tick(50); fix.detectChanges(); - // Verify expressions in the child query - QueryBuilderFunctions.verifyExpressionChipContent(fix, [0], 'ProductName', 'Contains', 'a', 1); - QueryBuilderFunctions.verifyExpressionChipContent(fix, [1], 'Released', 'False', '', 1); + // Verify parent is enabled + parentCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix); + ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement, false); })); it('Should collapse nested query when it is committed.', fakeAsync(() => { From 420656a5eba7c2b55f52d3006977ebbb4208e65a Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 26 Sep 2024 14:49:51 +0300 Subject: [PATCH 070/147] feat(query-builder): add checkbox and prop whether to show entity change dialog --- .../src/i18n/BG/query-builder-resources.ts | 5 +++++ .../src/i18n/CS/query-builder-resources.ts | 5 +++++ .../src/i18n/DA/query-builder-resources.ts | 5 +++++ .../src/i18n/DE/query-builder-resources.ts | 5 +++++ .../src/i18n/ES/query-builder-resources.ts | 5 +++++ .../src/i18n/FR/query-builder-resources.ts | 5 +++++ .../src/i18n/HU/query-builder-resources.ts | 5 +++++ .../src/i18n/IT/query-builder-resources.ts | 5 +++++ .../src/i18n/JA/query-builder-resources.ts | 5 +++++ .../src/i18n/KO/query-builder-resources.ts | 5 +++++ .../src/i18n/NB/query-builder-resources.ts | 5 +++++ .../src/i18n/NL/query-builder-resources.ts | 5 +++++ .../src/i18n/PL/query-builder-resources.ts | 5 +++++ .../src/i18n/PT/query-builder-resources.ts | 5 +++++ .../src/i18n/RO/query-builder-resources.ts | 5 +++++ .../src/i18n/SV/query-builder-resources.ts | 5 +++++ .../src/i18n/TR/query-builder-resources.ts | 5 +++++ .../i18n/ZH-HANS/query-builder-resources.ts | 5 +++++ .../i18n/ZH-HANT/query-builder-resources.ts | 5 +++++ .../lib/core/i18n/query-builder-resources.ts | 10 +++++++++ .../query-builder-tree.component.html | 16 ++++++++++---- .../query-builder-tree.component.ts | 15 +++++++++++-- .../query-builder.component.html | 1 + .../query-builder/query-builder.component.ts | 21 ++++++++++++++++--- 24 files changed, 149 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts index dc5a184e613..cc65d258009 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts @@ -60,6 +60,11 @@ const QueryBuilderResourceStringsBG_: ExpandRequire @@ -603,10 +604,17 @@
+
{{ this.resourceStrings.igx_query_builder_dialog_message }}
+ + {{ this.resourceStrings.igx_query_builder_dialog_checkbox_text }} +
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 028e6d3f7c6..ebcc56a6121 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -47,12 +47,13 @@ import { IgxIconButtonDirective } from '../directives/button/icon-button.directi import { IgxComboComponent } from "../combo/combo.component"; import { IgxLabelDirective } from '../input-group/public_api'; import { IgxComboHeaderDirective } from '../combo/public_api'; -import { IgxCheckboxComponent } from "../checkbox/checkbox.component"; +import { IChangeCheckboxEventArgs, IgxCheckboxComponent } from "../checkbox/checkbox.component"; import { IgxDialogComponent } from "../dialog/dialog.component"; import { ISelectionEventArgs } from '../drop-down/drop-down.common'; import { IgxTooltipDirective } from '../directives/tooltip/tooltip.directive'; import { IgxTooltipTargetDirective } from '../directives/tooltip/tooltip-target.directive'; import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives'; +import { IgxQueryBuilderComponent } from 'igniteui-angular'; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; @@ -171,6 +172,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @Input() public entities: EntityType[]; + @Input() + public queryBuilder: IgxQueryBuilderComponent; + @Input() public searchValueTemplate: TemplateRef = null; @@ -551,13 +555,20 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { public onEntitySelectChanging(event: ISelectionEventArgs) { event.cancel = true; this._entityNewValue = event.newSelection.value; - if (event.oldSelection.value) { + if (event.oldSelection.value && this.queryBuilder.showEntityChangeDialog) { this.entityChangeDialog.open(); } else { this.onEntityChangeConfirm(); } } + /** + * @hidden + */ + public onShowEntityChangeDialogChange(eventArgs: IChangeCheckboxEventArgs) { + this.queryBuilder.showEntityChangeDialog = !eventArgs.checked; + } + /** * @hidden */ diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html index cb7d18f8502..ff7b0dbe7dc 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.html @@ -11,6 +11,7 @@ [expressionTree]="this.expressionTree" [locale]="this.locale" [resourceStrings]="this.resourceStrings" + [queryBuilder]="this" (expressionTreeChange)="onExpressionTreeChange($event)" [searchValueTemplate]="searchValueTemplate"> diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index 4eee1e91a74..80dfa2ef735 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -1,4 +1,4 @@ -import { ContentChild, EventEmitter, Output, TemplateRef } from '@angular/core'; +import { booleanAttribute, ContentChild, EventEmitter, Output, TemplateRef } from '@angular/core'; import { NgIf, NgTemplateOutlet} from '@angular/common'; import { Component, Input, ViewChild, ElementRef, OnDestroy, HostBinding @@ -45,22 +45,37 @@ export class IgxQueryBuilderComponent implements OnDestroy { @HostBinding('style.display') public display = 'block'; + /** + * Gets/sets whether the confirmation dialog should be shown when changing entity. + * Default value is `true`. + */ + @Input({ transform: booleanAttribute }) + public showEntityChangeDialog = true; + + /** + * Gets/sets the entities. + */ @Input() public entities: EntityType[]; /** - * Returns the fields. - */ + * Returns the fields. + * @hidden + * @deprecated in version 18.2.0. Use the `entities` property instead. + */ public get fields(): FieldType[] { return this._fields; } /** * Sets the fields. + * @hidden + * @deprecated in version 18.2.0. Use the `entities` property instead. */ @Input() public set fields(fields: FieldType[]) { if (fields) { + this._fields = fields; this.entities = [ { name: null, From 715256af703579c4624a2a74674837cc97ac8a17 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Thu, 26 Sep 2024 16:09:36 +0300 Subject: [PATCH 071/147] docs(query-builder): Documented breaking change in IFilteringExpression --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5797f1296d8..512a6c05fdc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes for each version of this project will be documented in this file. +## 18.2.0 +- `IFilteringExpression` + - **Breaking Change** There is a new `conditionName` property which is required. This would generally be equal to the old `condition.name`. + ## 18.2.0 ### General - `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`, `IgxPivotGrid` From c72041a55c827b906f51737ccd69965fbbbc1760 Mon Sep 17 00:00:00 2001 From: teodosia Date: Thu, 26 Sep 2024 17:38:47 +0300 Subject: [PATCH 072/147] test(query-builder): add showEntityChangeDialog tests --- .../query-builder.component.spec.ts | 125 +++++++++++++++++- src/app/query-builder/query-builder.sample.ts | 14 +- 2 files changed, 130 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index 154e0e206e4..deeaf4ec8fa 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -1333,10 +1333,11 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyChildrenSelection(QueryBuilderFunctions.getQueryBuilderTreeItem(fix, [0]) as HTMLElement, false); })); - it('Should display an alert dialog when the entity is changed.', fakeAsync(() => { + it('Should display an alert dialog when the entity is changed and showEntityChangeDialog is true.', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; const queryTreeElement = queryBuilderElement.querySelector(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`); const dialog = queryTreeElement.querySelector('igx-dialog'); + const dialogOutlet = document.querySelector('.igx-dialog__window'); expect(dialog).toBeDefined(); // Click the initial 'Add Or Group' button. @@ -1359,6 +1360,108 @@ describe('IgxQueryBuilder', () => { // Alert dialog should be opened expect(dialog.checkVisibility()).toBeTrue(); + + // Show again checkbox should be unchecked + const checkbox = dialogOutlet.querySelector('igx-checkbox'); + expect(checkbox).toBeDefined(); + expect(checkbox).not.toHaveClass('igx-checkbox--checked'); + expect(queryBuilder.showEntityChangeDialog).toBeTrue(); + + // Close dialog + const cancelButton = Array.from(dialogOutlet.querySelectorAll('button'))[0]; + cancelButton.click(); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Alert dialog should NOT be opened + expect(dialog.checkVisibility()).toBeTrue(); + })); + + it('Should not display an alert dialog when the entity changed once showEntityChangeDialog is disabled.', fakeAsync(() => { + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const queryTreeElement = queryBuilderElement.querySelector(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`); + const dialog = queryTreeElement.querySelector('igx-dialog'); + const dialogOutlet = document.querySelector('.igx-dialog__window'); + expect(dialog).toBeDefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + // Alert dialog should not be opened if there is no previous selection + expect(dialog.checkVisibility()).toBeFalse(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Alert dialog should be opened + expect(dialog.checkVisibility()).toBeTrue(); + + // Check show again checkbox + const checkbox = dialogOutlet.querySelector('igx-checkbox') as HTMLElement; + expect(checkbox).toBeDefined(); + + checkbox.click(); + tick(100); + fix.detectChanges(); + expect(checkbox).toHaveClass('igx-checkbox--checked'); + expect(queryBuilder.showEntityChangeDialog).toBeFalse(); + + // Close dialog + const cancelButton = Array.from(dialogOutlet.querySelectorAll('button'))[0]; + cancelButton.click(); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Alert dialog should NOT be opened + expect(dialog.checkVisibility()).toBeFalse(); + })); + + it('Initially should not display an alert dialog when the entity is changed if hideEntityChangeDialog is disabled through API.', fakeAsync(() => { + queryBuilder.showEntityChangeDialog = false; + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const queryTreeElement = queryBuilderElement.querySelector(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`); + const dialog = queryTreeElement.querySelector('igx-dialog'); + expect(dialog).toBeDefined(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + // Alert dialog should not be opened if there is no previous selection + expect(dialog.checkVisibility()).toBeFalse(); + + // Select entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + + // Alert dialog should NOT be opened + expect(dialog.checkVisibility()).toBeFalse(); })); it('Should reset all inputs when the entity is changed.', fakeAsync(() => { @@ -1963,7 +2066,12 @@ describe('IgxQueryBuilder', () => { igx_query_builder_ungroup: 'My ungroup', igx_query_builder_delete: 'My delete', igx_query_builder_delete_filters: 'My delete filters', - igx_query_builder_initial_text: 'My initial text' + igx_query_builder_initial_text: 'My initial text', + igx_query_builder_dialog_title: 'My Confirmation', + igx_query_builder_dialog_message: 'My changing entity message', + igx_query_builder_dialog_checkbox_text: 'My do not show this dialog again', + igx_query_builder_dialog_cancel: 'My Cancel', + igx_query_builder_dialog_confirm: 'My Confirm' }); fix.detectChanges(); @@ -1990,6 +2098,19 @@ describe('IgxQueryBuilder', () => { // Populate edit inputs. QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products'. + // Show changing entity alert dialog + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + const dialogOutlet = document.querySelector('.igx-dialog__window'); + expect(dialogOutlet).toBeDefined(); + + expect(dialogOutlet.querySelector('.igx-dialog__window-title').textContent.trim()).toBe('My Confirmation'); + expect(dialogOutlet.querySelector('.igx-dialog__window-content').children[0].textContent.trim()).toBe('My changing entity message'); + expect(dialogOutlet.querySelector('.igx-dialog__window-content').children[1].textContent.trim()).toBe('My do not show this dialog again'); + expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[0].textContent.trim()).toBe('My Cancel'); + expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[1].textContent.trim()).toBe('My Confirm'); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 829ce9fa8af..9d8da85b55a 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -8,7 +8,8 @@ import { IgxButtonDirective, IgxButtonGroupComponent, IgxRippleDirective, - IgxQueryBuilderHeaderComponent} from 'igniteui-angular'; + IgxQueryBuilderHeaderComponent +} from 'igniteui-angular'; import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; import { CommonModule } from '@angular/common'; @@ -36,7 +37,6 @@ export class QueryBuilderComponent implements OnInit { public displayDensities; public expressionTree: IExpressionTree; public queryResult: string; - private backendUrl = "http://localhost:3333/"; public ngOnInit(): void { this.assaysFields = [ @@ -89,11 +89,11 @@ export class QueryBuilderComponent implements OnInit { const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Compounds', ['*']); tree.filteringOperands.push({ - fieldName: 'Id', - condition: IgxStringFilteringOperand.instance().condition('in'), - conditionName: IgxStringFilteringOperand.instance().condition('in').name, - searchTree: innerTree - }); + fieldName: 'Id', + condition: IgxStringFilteringOperand.instance().condition('in'), + conditionName: IgxStringFilteringOperand.instance().condition('in').name, + searchTree: innerTree + }); tree.filteringOperands.push({ fieldName: 'Id', condition: IgxStringFilteringOperand.instance().condition('equals'), From 94c57f8e8748aea6ec466635d201c3752d1a467a Mon Sep 17 00:00:00 2001 From: teodosia Date: Fri, 27 Sep 2024 10:11:57 +0300 Subject: [PATCH 073/147] test(query-builder): add context menu and/or buttons resource strings --- .../src/lib/query-builder/query-builder.component.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index deeaf4ec8fa..3a8816e903a 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -2060,6 +2060,8 @@ describe('IgxQueryBuilder', () => { igx_query_builder_end_group: 'My end group', igx_query_builder_create_and_group: 'My create and group', igx_query_builder_create_or_group: 'My create or group', + igx_query_builder_filter_operator_and: 'My and', + igx_query_builder_filter_operator_or: 'My or', igx_query_builder_and_label: 'My and', igx_query_builder_or_label: 'My or', igx_query_builder_add_condition: 'My condition', From ebe840719dd3dd7a41364eaa835790208d1a3843 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 27 Sep 2024 15:46:04 +0300 Subject: [PATCH 074/147] fix(query-builder): fix searchValueTemplate is now propagated to child trees --- .../src/lib/query-builder/query-builder-tree.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 538f9ec1d25..4fe9c794ecd 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -389,7 +389,8 @@
[queryBuilder]="this.queryBuilder" [parentExpression]="expressionItem" [expressionTree]="expressionItem.expression.searchTree" - (inEditModeChange)="onInEditModeChanged($event)"> + (inEditModeChange)="onInEditModeChanged($event)" + [searchValueTemplate]="searchValueTemplate"> From 5f22eccde9b0553803a90f63f93ae87ed8593e56 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 27 Sep 2024 19:03:03 +0300 Subject: [PATCH 075/147] fix(query-builder): fix circular dependancy issue --- .../src/lib/query-builder/query-builder-tree.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index f930ebc6f03..9704d9b2c31 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -53,7 +53,7 @@ import { ISelectionEventArgs } from '../drop-down/drop-down.common'; import { IgxTooltipDirective } from '../directives/tooltip/tooltip.directive'; import { IgxTooltipTargetDirective } from '../directives/tooltip/tooltip-target.directive'; import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives'; -import { IgxQueryBuilderComponent } from 'igniteui-angular'; +import { IgxQueryBuilderComponent } from './query-builder.component'; const DEFAULT_PIPE_DATE_FORMAT = 'mediumDate'; const DEFAULT_PIPE_TIME_FORMAT = 'mediumTime'; From 649b9f8fcd7e78d5b4d4027c7dad31603cb48d2d Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Mon, 30 Sep 2024 16:42:38 +0300 Subject: [PATCH 076/147] feat(query-builder): add funct. for using default template in the custom one --- .../query-builder-tree.component.html | 10 ++++----- .../query-builder-tree.component.ts | 22 ++++++++++++------- .../query-builder.component.spec.ts | 4 ++-- .../query-builder/query-builder.sample.html | 15 ++++++++++--- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 4fe9c794ecd..1ad6932e79f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -249,7 +249,7 @@
@@ -282,14 +282,14 @@
? 'number' : 'text' " - [(ngModel)]="searchValue" + [(ngModel)]="searchValue.value" /> #input igxInput tabindex="0" - [(ngModel)]="searchValue" + [(ngModel)]="searchValue.value" [disabled]=" !selectedField || !selectedCondition || diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 9704d9b2c31..c8f74bf8b69 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -424,7 +424,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * @hidden @internal */ - public searchValue: any; + public searchValue: { value: any } = { value: null }; /** * @hidden @internal @@ -603,7 +603,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedField = null; this.selectedCondition = null; - this.searchValue = null; + this.searchValue.value = null; this.entityChangeDialog.close(); this.entitySelect.close(); @@ -647,7 +647,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedField = value; if (oldValue && this._selectedField && this._selectedField.dataType !== oldValue.dataType) { this.selectedCondition = null; - this.searchValue = null; + this.searchValue.value = null; this.cdr.detectChanges(); } } @@ -734,7 +734,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public commitOperandEdit() { - const actualSearchValue = this.searchValueTemplate ? this._editedExpression.expression.searchVal : this.searchValue; + const actualSearchValue = this.searchValue.value; if (this._editedExpression) { this._editedExpression.expression.fieldName = this.selectedField.field; this._editedExpression.expression.condition = this.selectedField.filters.condition(this.selectedCondition); @@ -760,6 +760,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._editedExpression.expression.searchTree = null; } + if (this.selectedField.filters.condition(this.selectedCondition).isUnary) { + this._editedExpression.expression.searchVal = null; + } + this._editedExpression.inEditMode = false; this._editedExpression = null; } @@ -811,7 +815,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; return this.selectedField && this.selectedCondition && ( - ((!!this.searchValue || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || + ((!!this.searchValue.value || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || (innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined) || this.selectedField.filters.condition(this.selectedCondition).isUnary ); @@ -895,7 +899,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { expressionItem.expression.condition ? expressionItem.expression.condition.name : null; - this.searchValue = expressionItem.expression.searchVal; + this.searchValue.value = expressionItem.expression.searchVal; expressionItem.inEditMode = true; this._editedExpression = expressionItem; @@ -1233,10 +1237,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } - public getSearchValueTemplateContext(): any { + public getSearchValueTemplateContext(defaultSearchValueTemplate): any { const ctx = { $implicit: this.searchValue, - expression: this._editedExpression.expression + selectedField: this.selectedField, + selectedCondition: this.selectedCondition, + defaultSearchValueTemplate: defaultSearchValueTemplate }; return ctx; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index 3a8816e903a..25b4fb43c42 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -2211,8 +2211,8 @@ export class IgxQueryBuilderSampleTestComponent implements OnInit { template: ` - - + + `, diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index afa0e703adc..86392e47a72 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -10,9 +10,18 @@ - + + @@ -349,7 +347,7 @@
*ngIf="selectedField && selectedField.dataType === 'dateTime'" type="box" > - + Date: Fri, 4 Oct 2024 16:16:58 +0300 Subject: [PATCH 092/147] test(query-builder): fix failing tests --- .../query-builder/query-builder-functions.spec.ts | 2 +- .../query-builder/query-builder.component.spec.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index 5b14beff163..a8efb9f75ef 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -83,7 +83,7 @@ export class QueryBuilderFunctions { public static getQueryBuilderHeaderText(fix: ComponentFixture) { const header = QueryBuilderFunctions.getQueryBuilderHeader(fix); - const title = header.querySelector('.ig-typography__h6'); + const title = header.querySelector('.igx-query-builder__title'); return title.textContent; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index af857f95e30..d016995b0c9 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -1680,12 +1680,12 @@ describe('IgxQueryBuilder', () => { (item.querySelector('.igx-filter-tree__expression-condition') as HTMLElement).innerText == 'Not In' )[0]; const toggleBtn = expandableItem.querySelector('.igx-filter-tree__details-button') as HTMLElement; - expect((toggleBtn.querySelector('igx-icon') as HTMLElement).innerText).toBe('unfold_less'); + expect((toggleBtn.querySelector('igx-icon') as HTMLElement).innerText).toBe('unfold_more'); toggleBtn.click(); tick(100); fix.detectChanges(); - expect((toggleBtn.querySelector('igx-icon') as HTMLElement).innerText).toBe('unfold_more'); + expect((toggleBtn.querySelector('igx-icon') as HTMLElement).innerText).toBe('unfold_less'); expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeTrue(); })); @@ -2114,8 +2114,8 @@ describe('IgxQueryBuilder', () => { expect(dialogOutlet).toBeDefined(); expect(dialogOutlet.querySelector('.igx-dialog__window-title').textContent.trim()).toBe('My Confirmation'); - expect(dialogOutlet.querySelector('.igx-dialog__window-content').children[0].textContent.trim()).toBe('My changing entity message'); - expect(dialogOutlet.querySelector('.igx-dialog__window-content').children[1].textContent.trim()).toBe('My do not show this dialog again'); + expect(dialogOutlet.querySelector('.igx-query-builder-dialog').children[0].textContent.trim()).toBe('My changing entity message'); + expect(dialogOutlet.querySelector('.igx-query-builder-dialog').children[1].textContent.trim()).toBe('My do not show this dialog again'); expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[0].textContent.trim()).toBe('My Cancel'); expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[1].textContent.trim()).toBe('My Confirm'); @@ -2217,10 +2217,10 @@ export class IgxQueryBuilderSampleTestComponent implements OnInit { template: ` - From deabe57e99e1d773937bbaf3fc95f60a9aae621b Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 4 Oct 2024 16:31:39 +0300 Subject: [PATCH 093/147] fix(query-builder): adjust tooltip and fix details accessibility --- .../query-builder/query-builder-tree.component.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 044fc7000f2..03bb1850948 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -133,7 +133,7 @@
{{ expressionItem.expression.searchTree ? - expressionItem.expression.searchTree.returnFields : + expressionItem.expression.searchTree.returnFields.join(', ') : expressionItem.expression.condition.isUnary ? getConditionFriendlyName(expressionItem.expression.condition.name) : expressionItem.expression.searchVal @@ -174,8 +174,12 @@
-
+
{{this.resourceStrings.igx_query_builder_details}} From 0b9f9dec475a2b24e7115209d23d701e8b1aed95 Mon Sep 17 00:00:00 2001 From: desig9stein Date: Fri, 4 Oct 2024 17:05:10 +0300 Subject: [PATCH 094/147] fix(query-builder): fix chip size --- .../src/lib/core/styles/components/chip/_chip-theme.scss | 4 +++- .../components/query-builder/_query-builder-theme.scss | 6 +++++- .../lib/query-builder/query-builder-tree.component.html | 7 ++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/chip/_chip-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/chip/_chip-theme.scss index cc1693e30ac..b39f807ee2b 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/chip/_chip-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/chip/_chip-theme.scss @@ -711,7 +711,9 @@ %igx-chip__prefix, %igx-chip__suffix { - display: inline-flex; + @include ellipsis(); + + display: inline-block; vertical-align: middle; max-width: $chip-max-width; diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 23412bde296..c621ed91e80 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -293,10 +293,14 @@ } } - .igx-filter-tree__expression-column { + %filter-tree__expression-column { padding-inline: pad-inline(rem(3px), rem(6px), rem(8px)); } + %filter-tree__expression-condition { + padding-inline-start: pad-inline(rem(3px), rem(6px), rem(8px)); + } + igx-prefix { display: flex; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 03bb1850948..d5b70318b3b 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -93,17 +93,14 @@
{{expressionItem.fieldLabel || expressionItem.expression.fieldName}} - + {{ getConditionFriendlyName( expressionItem.expression.condition.name ) }} - + {{expressionItem.expression.searchTree.entity}} / {{formatReturnFields(expressionItem.expression.searchTree)}} From 6d66d3425c49feeddd56760a8d9ac586552529e5 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Mon, 7 Oct 2024 09:20:06 +0300 Subject: [PATCH 095/147] fix(query-builder): test details button is now tabble --- .../query-builder/query-builder-functions.spec.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index a8efb9f75ef..f3ee67d866e 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -668,15 +668,16 @@ export class QueryBuilderFunctions { case 2: expect(element).toHaveClass('igx-input-group__input'); break; case 3: expect(element).toHaveClass('igx-chip'); break; case 4: expect(element).toHaveClass('igx-chip__remove'); break; - case 5: expect(element).toHaveClass('igx-chip'); break; - case 6: expect(element).toHaveClass('igx-chip__remove'); break; - case 7: expect(element).toHaveClass('igx-chip'); break; - case 8: expect(element).toHaveClass('igx-chip__remove'); break; - case 9: expect(element).toHaveClass('igx-button'); - expect(element.innerText).toContain('Condition'); break; + case 5: expect(element).toHaveClass('igx-filter-tree__details-button'); break; + case 6: expect(element).toHaveClass('igx-chip'); break; + case 7: expect(element).toHaveClass('igx-chip__remove'); break; + case 8: expect(element).toHaveClass('igx-chip'); break; + case 9: expect(element).toHaveClass('igx-chip__remove'); break; case 10: expect(element).toHaveClass('igx-button'); - expect(element.innerText).toContain('"And" Group'); break; + expect(element.innerText).toContain('Condition'); break; case 11: expect(element).toHaveClass('igx-button'); + expect(element.innerText).toContain('"And" Group'); break; + case 12: expect(element).toHaveClass('igx-button'); expect(element.innerText).toContain('"Or" Group'); break; } i++; From 9059feec27562a541a0a601aeb1f46bb37a80d76 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Mon, 7 Oct 2024 14:37:57 +0300 Subject: [PATCH 096/147] feat(query-builder): update dev sample to have all data types --- src/app/query-builder/query-builder.sample.ts | 125 +++++++++--------- src/styles/_variables.scss | 8 +- 2 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 9d8da85b55a..a583fb314d8 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -1,6 +1,6 @@ import { Component, ViewChild, OnInit } from '@angular/core'; import { - FilteringExpressionsTree, IgxStringFilteringOperand, + FilteringExpressionsTree, FilteringLogic, IgxQueryBuilderComponent, changei18n, @@ -8,7 +8,13 @@ import { IgxButtonDirective, IgxButtonGroupComponent, IgxRippleDirective, - IgxQueryBuilderHeaderComponent + IgxQueryBuilderHeaderComponent, + IgxNumberFilteringOperand, + IgxStringFilteringOperand, + IgxBooleanFilteringOperand, + IgxDateFilteringOperand, + IgxTimeFilteringOperand, + IgxDateTimeFilteringOperand } from 'igniteui-angular'; import { IgxResourceStringsFR } from 'igniteui-angular-i18n'; import { SizeSelectorComponent } from '../size-selector/size-selector.component'; @@ -32,62 +38,78 @@ export class QueryBuilderComponent implements OnInit { public searchValueTemplate: IgxQueryBuilderSearchValueTemplateDirective; public entities: Array; - public assaysFields: Array; - public compoundsFields: Array; + public fieldsEntityA: Array; + public fieldsEntityB: Array; public displayDensities; public expressionTree: IExpressionTree; public queryResult: string; public ngOnInit(): void { - this.assaysFields = [ + this.fieldsEntityA = [ { field: 'Id', dataType: 'number' }, - { field: 'CompoundId', dataType: 'number' }, { field: 'Name', dataType: 'string' }, - { field: 'EndpointName', dataType: 'string' }, - { field: 'EndpointValue', dataType: 'string' }, - { field: 'Date', dataType: 'date' } + { field: 'Validated', dataType: 'boolean' }, + { field: 'Date created', dataType: 'date' }, + { field: 'Time created', dataType: 'time' }, + { field: 'DateTime created', dataType: 'dateTime' } ]; - this.compoundsFields = [ - { field: 'Id', dataType: 'number' }, - { field: 'Structure', dataType: 'string' }, - { field: 'Date', dataType: 'date' } + this.fieldsEntityB = [ + { field: 'Number', dataType: 'number' }, + { field: 'String', dataType: 'string' }, + { field: 'Boolean', dataType: 'boolean' }, + { field: 'Date', dataType: 'date' }, + { field: 'Time', dataType: 'time' }, + { field: 'DateTime', dataType: 'dateTime' } ]; this.entities = [ { - name: 'Assays', - fields: this.assaysFields + name: 'Entity A', + fields: this.fieldsEntityA }, { - name: 'Compounds', - fields: this.compoundsFields + name: 'Entity B', + fields: this.fieldsEntityB } ]; - const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Assays', ['Id']); + const innerTree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Entity B', ['Number']); innerTree.filteringOperands.push({ - fieldName: 'Name', - condition: IgxStringFilteringOperand.instance().condition('equals'), - conditionName: IgxStringFilteringOperand.instance().condition('equals').name, - searchVal: 'Hepacity', - // ignoreCase: true + fieldName: 'Number', + condition: IgxNumberFilteringOperand.instance().condition('equals'), + conditionName: IgxNumberFilteringOperand.instance().condition('equals').name, + searchVal: 123 }); innerTree.filteringOperands.push({ - fieldName: 'EndpointName', + fieldName: 'String', condition: IgxStringFilteringOperand.instance().condition('equals'), conditionName: IgxStringFilteringOperand.instance().condition('equals').name, - searchVal: 'IC60', - // ignoreCase: true + searchVal: 'abc' }); - - const innerTree2 = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Assays', ['Name']); - innerTree2.filteringOperands.push({ - fieldName: 'Name', - condition: IgxStringFilteringOperand.instance().condition('null'), - conditionName: IgxStringFilteringOperand.instance().condition('null').name, - // ignoreCase: true + innerTree.filteringOperands.push({ + fieldName: 'Boolean', + condition: IgxBooleanFilteringOperand.instance().condition('true'), + conditionName: IgxBooleanFilteringOperand.instance().condition('true').name + }); + innerTree.filteringOperands.push({ + fieldName: 'Date', + condition: IgxDateFilteringOperand.instance().condition('before'), + conditionName: IgxDateFilteringOperand.instance().condition('before').name, + searchVal: new Date() + }); + innerTree.filteringOperands.push({ + fieldName: 'Time', + condition: IgxTimeFilteringOperand.instance().condition('before'), + conditionName: IgxTimeFilteringOperand.instance().condition('before').name, + searchVal: new Date() + }); + innerTree.filteringOperands.push({ + fieldName: 'DateTime', + condition: IgxDateTimeFilteringOperand.instance().condition('before'), + conditionName: IgxDateTimeFilteringOperand.instance().condition('before').name, + searchVal: new Date() }); - const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Compounds', ['*']); + const tree = new FilteringExpressionsTree(FilteringLogic.And, undefined, 'Entity A', ['*']); tree.filteringOperands.push({ fieldName: 'Id', condition: IgxStringFilteringOperand.instance().condition('in'), @@ -95,40 +117,11 @@ export class QueryBuilderComponent implements OnInit { searchTree: innerTree }); tree.filteringOperands.push({ - fieldName: 'Id', - condition: IgxStringFilteringOperand.instance().condition('equals'), - conditionName: IgxStringFilteringOperand.instance().condition('equals').name, - searchVal: '123', - ignoreCase: true + fieldName: 'Validated', + condition: IgxBooleanFilteringOperand.instance().condition('true'), + conditionName: IgxBooleanFilteringOperand.instance().condition('true').name }); - // tree.filteringOperands.push({ - // fieldName: 'Structure', - // condition: IgxStringFilteringOperand.instance().condition('in'), - // searchTree: innerTree2 - // }); - - // const orTree = new FilteringExpressionsTree(FilteringLogic.Or); - // orTree.filteringOperands.push({ - // fieldName: 'ID', - // condition: IgxStringFilteringOperand.instance().condition('contains'), - // searchVal: 'b', - // ignoreCase: true - // }); - // orTree.filteringOperands.push({ - // fieldName: 'CompanyName', - // condition: IgxStringFilteringOperand.instance().condition('contains'), - // searchVal: 'c', - // ignoreCase: true - // }); - // tree.filteringOperands.push(orTree); - // tree.filteringOperands.push({ - // fieldName: 'CompanyName', - // condition: IgxStringFilteringOperand.instance().condition('contains'), - // searchVal: 'd', - // ignoreCase: true - // }); - this.expressionTree = tree; // this.onChange(); } diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index cf782515de3..632665ad146 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -1,10 +1,10 @@ @use 'sass:map'; @use '../../projects/igniteui-angular/src/lib/core/styles/themes' as *; -$palette: $light-indigo-palette; -$schema: $light-indigo-schema; -$typeface: $indigo-typeface; -$type-scale: $indigo-type-scale; +$palette: $light-material-palette; +$schema: $light-material-schema; +$typeface: $material-typeface; +$type-scale: $material-type-scale; $variant: map.get($schema, '_meta', 'variant'); $background-color: var(--ig-gray-900-contrast); From 10b4c5b01422e38102cce5ef705a18683e9382a7 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Mon, 7 Oct 2024 15:33:13 +0300 Subject: [PATCH 097/147] chore(query-builder): Made conditionName optional --- CHANGELOG.md | 2 +- .../src/lib/data-operations/filtering-expression.interface.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb441b48da1..c2f78957535 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ All notable changes for each version of this project will be documented in this - Added support for showing/hiding the indicator controls (dots). Can be configured via the `indicators` property. Defaults to `true`. - `IFilteringExpression` - - **Breaking Change** There is a new `conditionName` property which is required. This would generally be equal to the old `condition.name`. + - A new optional property called `conditionName` has been introduced. This would generally be equal to the existing `condition.name`. #### Scrollbar: New CSS variables diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index b85cc0dff1e..d58cac6a602 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -16,7 +16,7 @@ export enum FilteringLogic { export declare interface IFilteringExpression { fieldName: string; condition?: IFilteringOperation; - conditionName: string; + conditionName?: string; searchVal?: Serializable; searchTree?: IExpressionTree; ignoreCase?: boolean; From 1993b3b664f1230645081d562e757d6c64d102c8 Mon Sep 17 00:00:00 2001 From: desig9stein Date: Mon, 7 Oct 2024 14:11:17 +0300 Subject: [PATCH 098/147] fix(query-builder): the action area size --- .../styles/components/query-builder/_query-builder-theme.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index c621ed91e80..9e1501ee59f 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -370,6 +370,7 @@ display: flex; gap: $gap; align-items: center; + width: auto; [igxIconButton] { transition: none; From a0842deb7b7651e6f0e695d3b985bfc43964fdd0 Mon Sep 17 00:00:00 2001 From: desig9stein Date: Mon, 7 Oct 2024 17:23:10 +0300 Subject: [PATCH 099/147] fix(query-builder): fix chip label opacity --- .../styles/components/query-builder/_query-builder-theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index 9e1501ee59f..af052afb7dc 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -336,7 +336,7 @@ } %filter-tree__expression-condition { - opacity: .7; + opacity: if($variant != 'indigo', .6, .8); } %filter-tree__buttons { From d7dfe817450f632741c473dc670876d61aeb2034 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 8 Oct 2024 10:45:08 +0300 Subject: [PATCH 100/147] feat(query-builder): migrate fields to entities --- .../migrations/update-18_2_0/changes/inputs.json | 14 ++++++++++++++ .../migrations/update-18_2_0/index.spec.ts | 16 ++++++++++++++++ .../migrations/update-18_2_0/index.ts | 8 +++++++- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 projects/igniteui-angular/migrations/update-18_2_0/changes/inputs.json diff --git a/projects/igniteui-angular/migrations/update-18_2_0/changes/inputs.json b/projects/igniteui-angular/migrations/update-18_2_0/changes/inputs.json new file mode 100644 index 00000000000..0ccc841c01c --- /dev/null +++ b/projects/igniteui-angular/migrations/update-18_2_0/changes/inputs.json @@ -0,0 +1,14 @@ +{ + "$schema": "../../common/schema/binding.schema.json", + "changes": [ + { + "name": "fields", + "replaceWith": "entities", + "valueTransform": "fields_to_entities", + "owner": { + "selector": "igx-query-builder", + "type": "component" + } + } + ] +} diff --git a/projects/igniteui-angular/migrations/update-18_2_0/index.spec.ts b/projects/igniteui-angular/migrations/update-18_2_0/index.spec.ts index 55d83a2439b..0f95e674f36 100644 --- a/projects/igniteui-angular/migrations/update-18_2_0/index.spec.ts +++ b/projects/igniteui-angular/migrations/update-18_2_0/index.spec.ts @@ -135,4 +135,20 @@ describe(`Update to ${version}`, () => { );` ); }); + + it('should replace QueryBuilder deprecated property `fields` with `entities`', async () => { + appTree.create(`/testSrc/appPrefix/component/test.component.html`, + ` + + ` + ); + + const tree = await schematicRunner.runSchematic(migrationName, { shouldInvokeLS: false }, appTree); + + expect(tree.readContent('/testSrc/appPrefix/component/test.component.html')).toEqual( + ` + + ` + ); + }); }); diff --git a/projects/igniteui-angular/migrations/update-18_2_0/index.ts b/projects/igniteui-angular/migrations/update-18_2_0/index.ts index 5ba349b9698..917d9bef8b1 100644 --- a/projects/igniteui-angular/migrations/update-18_2_0/index.ts +++ b/projects/igniteui-angular/migrations/update-18_2_0/index.ts @@ -3,12 +3,18 @@ import type { SchematicContext, Tree } from '@angular-devkit/schematics'; -import { UpdateChanges } from '../common/UpdateChanges'; +import { BoundPropertyObject, InputPropertyType, UpdateChanges } from '../common/UpdateChanges'; const version = '18.2.0'; export default (): Rule => async (host: Tree, context: SchematicContext) => { context.logger.info(`Applying migration for Ignite UI for Angular to version ${version}`); const update = new UpdateChanges(__dirname, host, context); + + update.addValueTransform('fields_to_entities', (args: BoundPropertyObject): void => { + args.bindingType = InputPropertyType.EVAL; + args.value = `[{ name: '', fields: ${args.value}}]`; + }); + update.applyChanges(); }; From 0320f7aae2388228a1f9c7c4f3491cd6552239c9 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 8 Oct 2024 10:46:38 +0300 Subject: [PATCH 101/147] chore(*): update to latest theming --- projects/igniteui-angular/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/package.json b/projects/igniteui-angular/package.json index 9322923365c..86f58501820 100644 --- a/projects/igniteui-angular/package.json +++ b/projects/igniteui-angular/package.json @@ -74,7 +74,7 @@ "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "uuid": "^9.0.0", - "igniteui-theming": "^14.2.0-beta.1", + "igniteui-theming": "^14.2.0-beta.2", "@igniteui/material-icons-extended": "^3.0.0" }, "peerDependencies": { From a235e1bb1251ae4573a0b58f996866ceab14c578 Mon Sep 17 00:00:00 2001 From: Mihail Cherkasov Date: Tue, 8 Oct 2024 11:16:02 +0300 Subject: [PATCH 102/147] chore(query-builder): Revert searchVal type to any in IFilteringExpression --- .../src/lib/data-operations/filtering-expression.interface.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts index d58cac6a602..3e308e0fe1c 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expression.interface.ts @@ -1,5 +1,4 @@ import { IFilteringOperation } from './filtering-condition'; -import { Serializable } from 'node:child_process'; import { IExpressionTree } from './filtering-expressions-tree'; /* mustCoerceToInt */ @@ -17,7 +16,7 @@ export declare interface IFilteringExpression { fieldName: string; condition?: IFilteringOperation; conditionName?: string; - searchVal?: Serializable; + searchVal?: any; searchTree?: IExpressionTree; ignoreCase?: boolean; } From 550685aa0156df99e221c7f933b45c8f761ee4bb Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 8 Oct 2024 14:49:10 +0300 Subject: [PATCH 103/147] fix(query-builder): disable date & time pickers when nested query --- .../query-builder/query-builder-tree.component.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index d5b70318b3b..5dce38a313e 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -302,7 +302,9 @@
!selectedCondition || (selectedField && selectedField.filters.condition(selectedCondition) - .isUnary) + .isUnary)|| + selectedCondition === 'in' || + selectedCondition === 'notIn' " [locale]="this.locale" [outlet]="pickerOutlet" @@ -329,7 +331,9 @@
!selectedCondition || (selectedField && selectedField.filters.condition(selectedCondition) - .isUnary) + .isUnary) || + selectedCondition === 'in' || + selectedCondition === 'notIn' " [locale]="this.locale" [outlet]="pickerOutlet" @@ -359,7 +363,9 @@
!selectedCondition || (selectedField && selectedField.filters.condition(selectedCondition) - .isUnary) + .isUnary) || + selectedCondition === 'in' || + selectedCondition === 'notIn' " [igxDateTimeEditor]="selectedField.defaultDateTimeFormat" /> From 67bc4fbd4c11504a5e79482708a2a530c137d8b8 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 8 Oct 2024 17:09:59 +0300 Subject: [PATCH 104/147] feat(query-builder): update changelog --- CHANGELOG.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2f78957535..0cbb107ba01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,26 @@ All notable changes for each version of this project will be documented in this - Introduced ability for Simple Combo to automatically select and retain valid input on "Tab" press enhancing user experience by streamlining data entry and reducing the need for manual selection improving form navigation. - `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid` - To streamline the sorting of columns with custom formats, a new `FormattedValuesSortingStrategy` has been introduced. This strategy simplifies the sorting process by allowing direct sorting based on formatted values, eliminating the need to extend the `DefaultSortingStrategy` or implement a custom `ISortingStrategy`. This enhancement improves the ease of handling sorting with custom column formatters. - - `IgxCarousel` - Added support for vertical alignment. Can be configured via the `vertical` property. Defaults to `false`. - Added support for showing/hiding the indicator controls (dots). Can be configured via the `indicators` property. Defaults to `true`. - +- `IgxQueryBuilder` + - Introduced ability to create nested queries by specifying IN/NOT IN operators. + - Added the `entities` property that accepts an array describing the entity name and an array of its fields. The `fields` input property has been deprecated and will be removed in a future version. Automatic migrations are available and will be applied on `ng update`. + - Add option to template the search value input: + ``` + + @if (selectedField?.field === 'Id' && selectedCondition === 'equals'){ + + } @else { + + } + + ``` - `IFilteringExpression` - A new optional property called `conditionName` has been introduced. This would generally be equal to the existing `condition.name`. From 3f9c3229bbc257476b974ab51765936887c3e7e1 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 8 Oct 2024 17:44:06 +0300 Subject: [PATCH 105/147] chore(*): update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cbb107ba01..0e9c3d2adf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,8 @@ All notable changes for each version of this project will be documented in this - Added support for showing/hiding the indicator controls (dots). Can be configured via the `indicators` property. Defaults to `true`. - `IgxQueryBuilder` - Introduced ability to create nested queries by specifying IN/NOT IN operators. - - Added the `entities` property that accepts an array describing the entity name and an array of its fields. The `fields` input property has been deprecated and will be removed in a future version. Automatic migrations are available and will be applied on `ng update`. - - Add option to template the search value input: + - Added the `entities` property that accepts an array of `EntityType` objects describing an entity with its name and an array of fields. The `fields` input property has been deprecated and will be removed in a future version. Automatic migrations are available and will be applied on `ng update`. + - Added option to template the search value input: ``` Date: Tue, 8 Oct 2024 17:54:01 +0300 Subject: [PATCH 106/147] feat(query-builder): add props descriptions --- .../filtering-expressions-tree.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts index af94993b58d..a45e5594488 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-expressions-tree.ts @@ -68,7 +68,7 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { /** * Sets/gets the field name of the column where the filtering expression is placed. * ```typescript - * gridExpressionTree.fieldName = 'Column Field'; + * gridExpressionTree.fieldName = 'Column Field'; * ``` * ```typescript * let columnField = expressionTree.fieldName; @@ -82,7 +82,7 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { /** * Sets/gets the type of the filtering expressions tree. * ```typescript - * gridExpressionTree.type = FilteringExpressionsTree.Advanced; + * gridExpressionTree.type = FilteringExpressionsTree.Advanced; * ``` * ```typescript * let type = expressionTree.type; @@ -92,8 +92,30 @@ export class FilteringExpressionsTree implements IFilteringExpressionsTree { */ public type?: FilteringExpressionsTreeType; + /** + * Sets/gets the entity. + * ```typescript + * gridExpressionsTree.entity = 'Entity A'; + * ``` + * ```typescript + * let entity = gridExpressionsTree.entity; + * ``` + * + * @memberof FilteringExpressionsTree + */ public entity?: string; + /** + * Sets/gets the return fields. + * ```typescript + * gridExpressionsTree.returnFields = ['Column Field 1', 'Column Field 2']; + * ``` + * ```typescript + * let returnFields = gridExpressionsTree.returnFields; + * ``` + * + * @memberof FilteringExpressionsTree + */ public returnFields?: string[]; constructor(operator: FilteringLogic, fieldName?: string, entity?: string, returnFields?: string[]) { From 5c9b2e1ecc0bd1106658f34abaefdd1aa2c53ca2 Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Wed, 9 Oct 2024 10:57:05 +0300 Subject: [PATCH 107/147] deps(theming): bump to latest beta --- package-lock.json | 10 +++++----- package.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16918952420..0948cc9c0a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@types/source-map": "0.5.2", "express": "^4.21.0", "fflate": "^0.8.1", - "igniteui-theming": "^14.2.0-beta.2", + "igniteui-theming": "^14.2.0-beta.3", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", @@ -13752,12 +13752,12 @@ } }, "node_modules/igniteui-theming": { - "version": "14.2.0-beta.2", - "resolved": "https://registry.npmjs.org/igniteui-theming/-/igniteui-theming-14.2.0-beta.2.tgz", - "integrity": "sha512-2KvyY5tSVVXonSqXsVWk4264HTaZRqgMvx4uHtqLbVZVMVwA/ZLXJwod3iDRDcR17u8oicq1Cz0BKHPsPlkIJA==", + "version": "14.2.0-beta.3", + "resolved": "https://registry.npmjs.org/igniteui-theming/-/igniteui-theming-14.2.0-beta.3.tgz", + "integrity": "sha512-2xfjcJ+JCBYBzvSkI+yYDuRrLcY2KdEwtcTGBQRAMifwkJydzTPwVUqMOABB5LFUp/K58vIZ2Fz+oMF2d+XCbQ==", "license": "MIT", "peerDependencies": { - "sass": "^1.58.1" + "sass": "^1.69.5" } }, "node_modules/igniteui-trial-watermark": { diff --git a/package.json b/package.json index 901aa39988a..8a2ff0c00ef 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "@types/source-map": "0.5.2", "express": "^4.21.0", "fflate": "^0.8.1", - "igniteui-theming": "^14.2.0-beta.2", + "igniteui-theming": "^14.2.0-beta.3", "igniteui-trial-watermark": "^3.0.2", "lodash-es": "^4.17.21", "rxjs": "^7.8.0", From b7a7e3460fa239e8e1246a566fe555ebb07786d3 Mon Sep 17 00:00:00 2001 From: desig9stein Date: Tue, 8 Oct 2024 17:23:07 +0300 Subject: [PATCH 108/147] fix(query-builder): replace expression icons with icon buttons to improve accessibility and interaction styles. --- .../query-builder/_query-builder-theme.scss | 13 ++------ .../query-builder-tree.component.html | 30 ++++++++----------- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index af052afb7dc..ac1c3e5ace0 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -323,16 +323,9 @@ display: inline-flex; } - igx-icon { - cursor: pointer; - color: color(null, 'gray', 500); - outline-style: none; - - &:hover, - &:focus { - color: color(null, 'gray', 800); - } - } + %igx-icon-button-display { + --size: #{sizable(rem(20px), rem(24px), if($variant != 'indigo', rem(32px), rem(28px)))} + }; } %filter-tree__expression-condition { diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 5dce38a313e..484724f84f9 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -144,22 +144,19 @@
expressionItem.hovered " > - - - - - edit + +
-
Date: Wed, 9 Oct 2024 11:45:09 +0300 Subject: [PATCH 109/147] fix(query-builder): make sure that the close dialog button is on the correct spot in different sizes --- .../components/query-builder/_query-builder-theme.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss index ac1c3e5ace0..7f092b29684 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/query-builder/_query-builder-theme.scss @@ -461,8 +461,9 @@ %filter-con-menu__close-btn { position: absolute; - top: rem(-18px); - inset-inline-end: rem(-18px); + top: 0; + inset-inline-start: 100%; + transform: translate(-50%, -50%); background-color: var-get($theme, 'background'); border: rem(1px) solid color(null, 'gray', 200); From 34c68bf7b098a07b2c42918c08695b7deddf8d29 Mon Sep 17 00:00:00 2001 From: ivanvpetrov <110455887+ivanvpetrov@users.noreply.github.com> Date: Wed, 9 Oct 2024 14:33:42 +0300 Subject: [PATCH 110/147] feat(query-builder): add isNested prop to IFilteringOperation (#14879) * feat(query-builder): add isNested prop added to IFilteringOperation * chore(query-builder): fix isNestedQuery is now optional prop * chore(query-builder): add isNestedQuery description in CHANGELOG * chore(query-builder): remove isNestedQuery= false since it's now optional * chore(query-builder): remove isNestedQuery= false since it's now optional * fix(query-builder): add isNestedQuery condition in newly added code * chore(query-builder): fix isNestedQuery parent class name * fix(query-builder): removed isNestedQuery change from pivot tests --------- Co-authored-by: INFRAGISTICS\IPetrov --- CHANGELOG.md | 2 + .../data-operations/filtering-condition.ts | 47 +++++++++++++------ .../query-builder-tree.component.html | 26 ++++------ .../query-builder-tree.component.ts | 4 +- .../query-builder.component.spec.ts | 2 + 5 files changed, 47 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25177018e25..ca6f1de7a50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ All notable changes for each version of this project will be documented in this - `IFilteringExpression` - A new optional property called `conditionName` has been introduced. This would generally be equal to the existing `condition.name`. +- `IFilteringOperation` + - A new optional property called `isNestedQuery` has been introduced. It's used to indicate whether the condition leads to a nested query creation. ### Themes - `Palettes` diff --git a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts index 7c65c15a4d7..7dd2a25d41a 100644 --- a/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts +++ b/projects/igniteui-angular/src/lib/data-operations/filtering-condition.ts @@ -22,12 +22,14 @@ export class IgxFilteringOperand { }, { name: 'in', isUnary: false, + isNestedQuery: true, iconName: 'in', logic: (target: any, searchVal: Set) => this.findValueInSet(target, searchVal) }, { name: 'notIn', isUnary: false, + isNestedQuery: true, iconName: 'not-in', logic: (target: any, searchVal: Set) => !this.findValueInSet(target, searchVal) }]; @@ -41,7 +43,7 @@ export class IgxFilteringOperand { * Returns an array of names of the conditions which are visible in the filtering UI */ public conditionList(): string[] { - return this.extendedConditionList().filter(c => c !== 'in' && c !== 'notIn'); + return this.operations.filter(f => !f.hidden && !f.isNestedQuery).map((element) => element.name); } /** @@ -86,7 +88,7 @@ export class IgxFilteringOperand { export class IgxBooleanFilteringOperand extends IgxFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'all', isUnary: true, iconName: 'filter_all', @@ -111,7 +113,9 @@ export class IgxBooleanFilteringOperand extends IgxFilteringOperand { isUnary: true, iconName: 'filter_not_empty', logic: (target: boolean) => target !== null && target !== undefined - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } } @@ -123,7 +127,7 @@ export class IgxBooleanFilteringOperand extends IgxFilteringOperand { class IgxBaseDateTimeFilteringOperand extends IgxFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'empty', isUnary: true, iconName: 'filter_empty', @@ -133,7 +137,9 @@ class IgxBaseDateTimeFilteringOperand extends IgxFilteringOperand { isUnary: true, iconName: 'filter_not_empty', logic: (target: Date) => target !== null && target !== undefined - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } /** @@ -204,7 +210,7 @@ class IgxBaseDateTimeFilteringOperand extends IgxFilteringOperand { export class IgxDateFilteringOperand extends IgxBaseDateTimeFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'equals', isUnary: false, iconName: 'filter_equal', @@ -404,7 +410,9 @@ export class IgxDateFilteringOperand extends IgxBaseDateTimeFilteringOperand { const now = IgxDateFilteringOperand.getDateParts(new Date(), 'y'); return d.year === now.year + 1; } - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } public override findValueInSet(target: any, searchVal: Set) { @@ -421,7 +429,7 @@ export class IgxDateFilteringOperand extends IgxBaseDateTimeFilteringOperand { export class IgxDateTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'equals', isUnary: false, iconName: 'filter_equal', @@ -623,7 +631,9 @@ export class IgxDateTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand const now = IgxDateTimeFilteringOperand.getDateParts(new Date(), 'y'); return d.year === now.year + 1; } - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } } @@ -631,7 +641,7 @@ export class IgxDateTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand export class IgxTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'at', isUnary: false, iconName: 'filter_equal', @@ -725,7 +735,9 @@ export class IgxTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand { targetn.hours > search.hours ? true : targetn.hours === search.hours && targetn.minutes > search.minutes ? true : targetn.hours === search.hours && targetn.minutes === search.minutes && targetn.seconds > search.seconds; } - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } /** @@ -748,7 +760,7 @@ export class IgxTimeFilteringOperand extends IgxBaseDateTimeFilteringOperand { export class IgxNumberFilteringOperand extends IgxFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'equals', isUnary: false, iconName: 'filter_equal', @@ -788,7 +800,9 @@ export class IgxNumberFilteringOperand extends IgxFilteringOperand { isUnary: true, iconName: 'filter_not_empty', logic: (target: number) => target !== null && target !== undefined && !isNaN(target) - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } } @@ -801,7 +815,7 @@ export class IgxNumberFilteringOperand extends IgxFilteringOperand { export class IgxStringFilteringOperand extends IgxFilteringOperand { protected constructor() { super(); - this.operations = [{ + const newOperations: IFilteringOperation[] = [{ name: 'contains', isUnary: false, iconName: 'filter_contains', @@ -865,7 +879,9 @@ export class IgxStringFilteringOperand extends IgxFilteringOperand { isUnary: true, iconName: 'filter_not_empty', logic: (target: string) => target !== null && target !== undefined && target.length > 0 - }].concat(this.operations); + }]; + + this.operations = newOperations.concat(this.operations); } /** @@ -890,6 +906,7 @@ export class IgxStringFilteringOperand extends IgxFilteringOperand { export interface IFilteringOperation { name: string; isUnary: boolean; + isNestedQuery?: boolean; iconName: string; hidden?: boolean; /* blazorCSSuppress */ diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index 484724f84f9..7f96d55cba6 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -271,10 +271,8 @@
!selectedField || !selectedCondition || (selectedField && - selectedField.filters.condition(selectedCondition) - .isUnary) || - selectedCondition === 'in' || - selectedCondition === 'notIn' + (selectedField.filters.condition(selectedCondition).isUnary || + selectedField.filters.condition(selectedCondition).isNestedQuery)) " [type]=" selectedField && selectedField.dataType === 'number' @@ -297,10 +295,8 @@
!selectedField || !selectedCondition || (selectedField && - selectedField.filters.condition(selectedCondition) - .isUnary)|| - selectedCondition === 'in' || - selectedCondition === 'notIn' + (selectedField.filters.condition(selectedCondition).isUnary || + selectedField.filters.condition(selectedCondition).isNestedQuery)) " [locale]="this.locale" [outlet]="pickerOutlet" @@ -326,10 +322,8 @@
!selectedField || !selectedCondition || (selectedField && - selectedField.filters.condition(selectedCondition) - .isUnary) || - selectedCondition === 'in' || - selectedCondition === 'notIn' + (selectedField.filters.condition(selectedCondition).isUnary || + selectedField.filters.condition(selectedCondition).isNestedQuery)) " [locale]="this.locale" [outlet]="pickerOutlet" @@ -358,10 +352,8 @@
!selectedField || !selectedCondition || (selectedField && - selectedField.filters.condition(selectedCondition) - .isUnary) || - selectedCondition === 'in' || - selectedCondition === 'notIn' + (selectedField.filters.condition(selectedCondition).isUnary || + selectedField.filters.condition(selectedCondition).isNestedQuery)) " [igxDateTimeEditor]="selectedField.defaultDateTimeFormat" /> @@ -385,7 +377,7 @@
- + q.isInEditMode())[0] - if (innerQuery && (this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) { + if (innerQuery && this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) { if (!this._editedExpression.expression.searchTree) { this._editedExpression.expression.searchTree = new FilteringExpressionsTree(innerQuery.expressionTree.operator); } @@ -815,7 +815,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; return this.selectedField && this.selectedCondition && ( - ((!!this.searchValue.value || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedCondition === 'in' || this.selectedCondition === 'notIn')) || + ((!!this.searchValue.value || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery)) || (innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined) || this.selectedField.filters.condition(this.selectedCondition).isUnary ); diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index d016995b0c9..6596baa9071 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -1014,6 +1014,7 @@ describe('IgxQueryBuilder', () => { "condition": { "name": "in", "isUnary": false, + "isNestedQuery": true, "iconName": "in" }, "conditionName": null, @@ -1097,6 +1098,7 @@ describe('IgxQueryBuilder', () => { "condition": { "name": "notIn", "isUnary": false, + "isNestedQuery": true, "iconName": "not-in" }, "conditionName": null, From 248a07293ff49eb549a4ce054a139bf8efa1688a Mon Sep 17 00:00:00 2001 From: Rumyana Andriova <54146583+randriova@users.noreply.github.com> Date: Wed, 9 Oct 2024 17:07:57 +0300 Subject: [PATCH 111/147] feat(i18n): Updating localization --- .../src/i18n/BG/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/CS/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/DA/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/DE/query-builder-resources.ts | 24 ++++++++-------- .../src/i18n/ES/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/FR/query-builder-resources.ts | 26 ++++++++--------- .../src/i18n/HU/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/IT/query-builder-resources.ts | 26 ++++++++--------- .../src/i18n/JA/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/KO/query-builder-resources.ts | 24 ++++++++-------- .../src/i18n/NB/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/NL/query-builder-resources.ts | 24 ++++++++-------- .../src/i18n/PL/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/PT/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/RO/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/SV/query-builder-resources.ts | 28 +++++++++---------- .../src/i18n/TR/query-builder-resources.ts | 28 +++++++++---------- .../i18n/ZH-HANS/query-builder-resources.ts | 28 +++++++++---------- .../i18n/ZH-HANT/query-builder-resources.ts | 28 +++++++++---------- 19 files changed, 258 insertions(+), 258 deletions(-) diff --git a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts index b7b5bfab3df..1d40caa084b 100644 --- a/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts +++ b/projects/igniteui-angular-i18n/src/i18n/BG/query-builder-resources.ts @@ -3,7 +3,7 @@ import { IQueryBuilderResourceStrings } from 'igniteui-angular'; const QueryBuilderResourceStringsBG_: ExpandRequire = { igx_query_builder_date_placeholder: 'Избери дата', igx_query_builder_time_placeholder: 'Избери време', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Избери дата и час', igx_query_builder_filter_operator_and: 'And', igx_query_builder_filter_operator_or: 'Or', igx_query_builder_filter_contains: 'Съдържа', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsBG_: ExpandRequire = { igx_query_builder_date_placeholder: 'Datum výdeje', igx_query_builder_time_placeholder: 'Čas vyzvednutí', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Vyberte datum a čas', igx_query_builder_filter_operator_and: 'A', igx_query_builder_filter_operator_or: 'Nebo', igx_query_builder_filter_contains: 'Obsahuje', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsCS_: ExpandRequire = { igx_query_builder_date_placeholder: 'Afhentningsdato', igx_query_builder_time_placeholder: 'Afhentningstidspunkt', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Vælg dato og tid', igx_query_builder_filter_operator_and: 'Og', igx_query_builder_filter_operator_or: 'Eller', igx_query_builder_filter_contains: 'Indeholder', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsDA_: ExpandRequire = { igx_query_builder_date_placeholder: 'Wähle Datum', igx_query_builder_time_placeholder: 'Abholungszeit', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Datum und Uhrzeit auswählen', igx_query_builder_filter_operator_and: 'Und', igx_query_builder_filter_operator_or: 'Oder', igx_query_builder_filter_contains: 'Enthält', @@ -17,7 +17,7 @@ const QueryBuilderResourceStringsDE_: ExpandRequire = { igx_query_builder_date_placeholder: 'Elegir Fecha', igx_query_builder_time_placeholder: 'Hora de recogida', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Seleccionar fecha y hora', igx_query_builder_filter_operator_and: 'Y', igx_query_builder_filter_operator_or: 'O', igx_query_builder_filter_contains: 'Contiene', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsES_: ExpandRequire = { igx_query_builder_date_placeholder: 'Choisir la date', igx_query_builder_time_placeholder: 'Heure de récupération', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Sélectionner la date et l\'heure', igx_query_builder_filter_operator_and: 'Et', igx_query_builder_filter_operator_or: 'Ou', igx_query_builder_filter_contains: 'Contient', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsFR_: ExpandRequire = { igx_query_builder_date_placeholder: 'Felvétel dátuma', igx_query_builder_time_placeholder: 'Felvétel időpontja', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Dátum és idő kiválasztása', igx_query_builder_filter_operator_and: 'És', igx_query_builder_filter_operator_or: 'Vagy', igx_query_builder_filter_contains: 'Tartalmazza', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsHU_: ExpandRequire = { igx_query_builder_date_placeholder: 'Data di prelievo', igx_query_builder_time_placeholder: 'Ora ritiro', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Seleziona data e ora', igx_query_builder_filter_operator_and: 'E', igx_query_builder_filter_operator_or: 'O', igx_query_builder_filter_contains: 'Contiene', @@ -17,7 +17,7 @@ const QueryBuilderResourceStringsIT_: ExpandRequire = { igx_query_builder_date_placeholder: '日付の選択', igx_query_builder_time_placeholder: 'ピックアップ時間', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: '日付と時間の選択', igx_query_builder_filter_operator_and: 'And', igx_query_builder_filter_operator_or: 'Or', igx_query_builder_filter_contains: 'を含む', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsJA_: ExpandRequire = { igx_query_builder_date_placeholder: '픽업 날짜', igx_query_builder_time_placeholder: '인도 시간', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: '날짜 및 시간 선택', igx_query_builder_filter_operator_and: '그리고', igx_query_builder_filter_operator_or: '또는', igx_query_builder_filter_contains: '포함', @@ -55,17 +55,17 @@ const QueryBuilderResourceStringsKO_: ExpandRequire = { igx_query_builder_date_placeholder: 'Hentedato', igx_query_builder_time_placeholder: 'Hentetid', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Velg dato og tid', igx_query_builder_filter_operator_and: 'Og', igx_query_builder_filter_operator_or: 'Eller', igx_query_builder_filter_contains: 'Inneholder', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsNB_: ExpandRequire = { igx_query_builder_date_placeholder: 'Ophaaldatum', igx_query_builder_time_placeholder: 'Ophaaltijd', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Datum en tijd selecteren', igx_query_builder_filter_operator_and: 'En', igx_query_builder_filter_operator_or: 'Of', igx_query_builder_filter_contains: 'Bevat', @@ -17,7 +17,7 @@ const QueryBuilderResourceStringsNL_: ExpandRequire = { igx_query_builder_date_placeholder: 'Data odbioru', igx_query_builder_time_placeholder: 'Godzina odbioru', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Wybierz datę i godzinę', igx_query_builder_filter_operator_and: 'I', igx_query_builder_filter_operator_or: 'Lub', igx_query_builder_filter_contains: 'Zawiera', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsPL_: ExpandRequire = { igx_query_builder_date_placeholder: 'Data de recolha', igx_query_builder_time_placeholder: 'Hora do levantamento', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Selecionar data e hora', igx_query_builder_filter_operator_and: 'E', igx_query_builder_filter_operator_or: 'Ou', igx_query_builder_filter_contains: 'Contém', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsPT_: ExpandRequire = { igx_query_builder_date_placeholder: 'Data ridicării', igx_query_builder_time_placeholder: 'Ora preluării', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Selectează data și ora', igx_query_builder_filter_operator_and: 'Și', igx_query_builder_filter_operator_or: 'Sau', igx_query_builder_filter_contains: 'Conține', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsRO_: ExpandRequire = { igx_query_builder_date_placeholder: 'Upphämtningsdatum', igx_query_builder_time_placeholder: 'Upphämtningstid', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Välj datum och tid', igx_query_builder_filter_operator_and: 'Och', igx_query_builder_filter_operator_or: 'Eller', igx_query_builder_filter_contains: 'Innehåller', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsSV_: ExpandRequire = { igx_query_builder_date_placeholder: 'Alma tarihi', igx_query_builder_time_placeholder: 'Alma saati', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: 'Tarih ve saat seç', igx_query_builder_filter_operator_and: 'Ve', igx_query_builder_filter_operator_or: 'Veya', igx_query_builder_filter_contains: 'İçerir', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsTR_: ExpandRequire = { igx_query_builder_date_placeholder: '选择日期', igx_query_builder_time_placeholder: '取车时间', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: '选择日期和时间', igx_query_builder_filter_operator_and: '和', igx_query_builder_filter_operator_or: '或', igx_query_builder_filter_contains: '包含', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsZHHANS_: ExpandRequire = { igx_query_builder_date_placeholder: '領取日期', igx_query_builder_time_placeholder: '取車時間', - igx_query_builder_datetime_placeholder: 'Select date & time', + igx_query_builder_datetime_placeholder: '選取日期和時間', igx_query_builder_filter_operator_and: '和', igx_query_builder_filter_operator_or: '或', igx_query_builder_filter_contains: '包含', @@ -16,8 +16,8 @@ const QueryBuilderResourceStringsZHHANT_: ExpandRequire Date: Thu, 10 Oct 2024 11:29:20 +0300 Subject: [PATCH 112/147] test(query-builder): add thorough In/NotIn tests --- .../query-builder-functions.spec.ts | 3 +- .../query-builder.component.spec.ts | 57 ++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index f3ee67d866e..1b172f25324 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -32,7 +32,8 @@ export const SampleEntities = [ name: 'Orders', fields: [ { field: 'OrderId', dataType: 'number' }, { field: 'OrderName', dataType: 'string' }, - { field: 'OrderDate', dataType: 'date' } + { field: 'OrderDate', dataType: 'date' }, + { field: 'Delivered', dataType: 'boolean' } ] } ]; diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index 6596baa9071..f35a1dee5fd 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -83,7 +83,7 @@ describe('IgxQueryBuilder', () => { // entity select should have proper value expect(queryBuilder.queryTree.selectedEntity.name).toEqual(queryBuilder.expressionTree.entity); // fields input should have proper value - expect(queryBuilder.queryTree.selectedReturnFields.length).toEqual(3); + expect(queryBuilder.queryTree.selectedReturnFields.length).toEqual(4); // nested queries should be collapsed const nestedQueryTrees = queryTreeExpressionContainer.querySelectorAll(QueryBuilderConstants.QUERY_BUILDER_TREE); for (let i = 0; i < nestedQueryTrees.length; i++) { @@ -168,7 +168,8 @@ describe('IgxQueryBuilder', () => { "returnFields": [ "OrderId", "OrderName", - "OrderDate" + "OrderDate", + "Delivered" ] }`); })); @@ -603,7 +604,7 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); fix.detectChanges(); const dropdownItems = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement); - expect(dropdownItems.length).toBe(3); + expect(dropdownItems.length).toBe(4); expect((dropdownItems[0] as HTMLElement).innerText).toBe('OrderId'); expect((dropdownItems[1] as HTMLElement).innerText).toBe('OrderName'); expect((dropdownItems[2] as HTMLElement).innerText).toBe('OrderDate'); @@ -935,7 +936,8 @@ describe('IgxQueryBuilder', () => { "returnFields": [ "OrderId", "OrderName", - "OrderDate" + "OrderDate", + "Delivered" ] }`); })); @@ -1051,11 +1053,51 @@ describe('IgxQueryBuilder', () => { "returnFields": [ "OrderId", "OrderName", - "OrderDate" + "OrderDate", + "Delivered" ] }`); })); + it('Should disable value fields when isNestedQuery condition is selected', fakeAsync(() => { + //Run test for all data type fields of the Order entity + for (let i = 0; i <= 3; i++) { + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, i); // Select 'OrderId','OrderName','OrderDate','Delivered' column. + + let InConditionIndex; + switch(i){ + case 0: + case 1: InConditionIndex = 10; break;// for string and number + case 2: InConditionIndex = 16; break; //for date + case 3: InConditionIndex = 7; break; // for boolean + } + + //Verify 'In' disables value input and renders empty sub query + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, InConditionIndex); // Select 'In' operator. + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, false); + let nestedTree = fix.debugElement.query(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE)); + expect(nestedTree).toBeDefined(); + + //Verify 'NotIn' disables value input and renders empty sub query + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, InConditionIndex + 1); // Select 'NotIn' operator. + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, false); + nestedTree = fix.debugElement.query(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE)); + expect(nestedTree).toBeDefined(); + + const closeBtn = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix); + closeBtn.click(); + fix.detectChanges(); + } + })); + it('Should correctly apply an \'not-in\' column condition through UI.', fakeAsync(() => { // Verify there is no expression. expect(queryBuilder.expressionTree).toBeUndefined(); @@ -1135,7 +1177,8 @@ describe('IgxQueryBuilder', () => { "returnFields": [ "OrderId", "OrderName", - "OrderDate" + "OrderDate", + "Delivered" ] }`); })); @@ -1513,7 +1556,7 @@ describe('IgxQueryBuilder', () => { fix.detectChanges(); // Verify all inputs - QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate', '', '', ''); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); })); it('Should NOT reset all inputs when the entity is not changed.', fakeAsync(() => { From 5d267da1ca68bb8c80b9110f8c96f107cb4d97ae Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 10 Oct 2024 18:12:40 +0300 Subject: [PATCH 113/147] feat(query-builder): make some props of FieldType optional --- .../src/lib/grids/common/grid.interface.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts index 613d0036e8b..a0ff1aa3bcd 100644 --- a/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts +++ b/projects/igniteui-angular/src/lib/grids/common/grid.interface.ts @@ -302,12 +302,12 @@ export interface FieldType { header?: string; /* alternateType: GridColumnDataType */ dataType: DataType; - filters: IgxFilteringOperand; - pipeArgs: IFieldPipeArgs; - defaultTimeFormat: string; - defaultDateTimeFormat: string; + filters?: IgxFilteringOperand; + pipeArgs?: IFieldPipeArgs; + defaultTimeFormat?: string; + defaultDateTimeFormat?: string; - formatter(value: any, rowData?: any): any; + formatter?(value: any, rowData?: any): any; } /** From f8c2cac3dd5f3dc40d5405df78daf01d512bca68 Mon Sep 17 00:00:00 2001 From: teodosia Date: Tue, 15 Oct 2024 11:21:48 +0300 Subject: [PATCH 114/147] test(query-builder): fix failing AF tests --- .../grid/grid-filtering-advanced.spec.ts | 217 +++++++++--------- 1 file changed, 110 insertions(+), 107 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index 04b4e2a3df3..0f4a82e0b6c 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -24,6 +24,7 @@ import { IFilteringEventArgs } from '../public_api'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; import { QueryBuilderConstants, QueryBuilderFunctions } from '../../query-builder/query-builder-functions.spec'; import { By } from '@angular/platform-browser'; +import { IgxDateTimeEditorDirective } from '../../directives/date-time-editor/date-time-editor.directive'; describe('IgxGrid - Advanced Filtering #grid - ', () => { configureTestSuite((() => { @@ -633,10 +634,11 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); fix.detectChanges(); const dropdownItems = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement); - expect(dropdownItems.length).toBe(3); + expect(dropdownItems.length).toBe(4); expect((dropdownItems[0] as HTMLElement).innerText).toBe('HeaderID'); expect((dropdownItems[1] as HTMLElement).innerText).toBe('ProductName'); expect((dropdownItems[2] as HTMLElement).innerText).toBe('Another Field'); + expect((dropdownItems[3] as HTMLElement).innerText).toBe('ReleaseTime'); })); it('Should scroll the adding buttons into view when the add icon of a chip is clicked.', fakeAsync(() => { @@ -1050,112 +1052,113 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { expect(rows.length).toEqual(1, 'Wrong filtered rows count'); })); - // it('DateTime: Should set editorOptions.dateTimeFormat prop as inputFormat for the filter value editor', fakeAsync(() => { - // const releaseDateColumn = grid.getColumnByName('ReleaseDate'); - // releaseDateColumn.dataType = 'dateTime'; - // releaseDateColumn.editorOptions = { - // dateTimeFormat: 'dd-MM-yyyy HH:mm aaaaa' - // } - // fix.detectChanges(); - - // grid.openAdvancedFilteringDialog(); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - // tick(100); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringColumnSelect(fix); - // fix.detectChanges(); - // const dropdownItems = GridFunctions.getAdvancedFilteringSelectDropdownItems(fix); - // expect(dropdownItems[4].innerText).toBe('ReleaseDate'); - - // selectColumnInEditModeExpression(fix, 4); // Select 'ReleaseDate' column - // selectOperatorInEditModeExpression(fix, 0); - - // const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) - // .injector.get(IgxDateTimeEditorDirective); - // expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseDateColumn.editorOptions.dateTimeFormat); - // expect(dateTimeEditor.displayFormat.normalize('NFKC')).toMatch(releaseDateColumn.pipeArgs.format); - // expect(dateTimeEditor.locale).toMatch(grid.locale); - // })); - - // it('DateTime: Should set pipeArgs.format as inputFormat for the filter editor if numeric and editorOptions.dateTimeFormat not set', fakeAsync(() => { - // const releaseDateColumn = grid.getColumnByName('ReleaseDate'); - // releaseDateColumn.dataType = 'dateTime'; - // releaseDateColumn.pipeArgs = { - // format: 'dd-MM-yyyy HH:mm aaaaa' - // } - // fix.detectChanges(); - - // grid.openAdvancedFilteringDialog(); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - // tick(100); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringColumnSelect(fix); - // fix.detectChanges(); - - // selectColumnInEditModeExpression(fix, 4); - // selectOperatorInEditModeExpression(fix, 0); - - // const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) - // .injector.get(IgxDateTimeEditorDirective); - // expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseDateColumn.pipeArgs.format); - // expect(dateTimeEditor.displayFormat.normalize('NFKC')).toMatch(releaseDateColumn.pipeArgs.format); - // })); - - // it('Time: Should set editorOptions.dateTimeFormat prop as inputFormat for the filter value editor', fakeAsync(() => { - // const releaseTimeColumn = grid.getColumnByName('ReleaseTime'); - // releaseTimeColumn.editorOptions = { - // dateTimeFormat: 'hh:mm' - // } - // fix.detectChanges(); - - // grid.openAdvancedFilteringDialog(); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - // tick(100); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringColumnSelect(fix); - // fix.detectChanges(); - - // selectColumnInEditModeExpression(fix, 6); - // selectOperatorInEditModeExpression(fix, 0); - - // const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) - // .injector.get(IgxDateTimeEditorDirective); - // expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseTimeColumn.editorOptions.dateTimeFormat); - // })); - - // it('Time: Should set pipeArgs.format as inputFormat for the filter editor if numeric and editorOptions.dateTimeFormat not set', fakeAsync(() => { - // const releaseTimeColumn = grid.getColumnByName('ReleaseTime'); - // releaseTimeColumn.pipeArgs = { - // format: 'hh:mm' - // } - // fix.detectChanges(); - - // grid.openAdvancedFilteringDialog(); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fix, 0); - // tick(100); - // fix.detectChanges(); - - // GridFunctions.clickAdvancedFilteringColumnSelect(fix); - // fix.detectChanges(); - - // selectColumnInEditModeExpression(fix, 6); - // selectOperatorInEditModeExpression(fix, 0); - - // const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) - // .injector.get(IgxDateTimeEditorDirective); - // expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseTimeColumn.pipeArgs.format); - // })); + it('DateTime: Should set editorOptions.dateTimeFormat prop as inputFormat for the filter value editor', fakeAsync(() => { + const releaseDateColumn = grid.getColumnByName('ReleaseDate'); + releaseDateColumn.dataType = 'dateTime'; + releaseDateColumn.editorOptions = { + dateTimeFormat: 'dd-MM-yyyy HH:mm aaaaa' + } + fix.detectChanges(); + + grid.openAdvancedFilteringDialog(); + fix.detectChanges(); + + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); + fix.detectChanges(); + const dropdownItems = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement); + expect((dropdownItems[4] as HTMLElement).innerText).toBe('ReleaseDate'); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 4); // Select 'ReleaseDate' column + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + + const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) + .injector.get(IgxDateTimeEditorDirective); + expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseDateColumn.editorOptions.dateTimeFormat); + expect(dateTimeEditor.displayFormat.normalize('NFKC')).toMatch(releaseDateColumn.pipeArgs.format); + expect(dateTimeEditor.locale).toMatch(grid.locale); + })); + + it('DateTime: Should set pipeArgs.format as inputFormat for the filter editor if numeric and editorOptions.dateTimeFormat not set', fakeAsync(() => { + const releaseDateColumn = grid.getColumnByName('ReleaseDate'); + releaseDateColumn.dataType = 'dateTime'; + releaseDateColumn.pipeArgs = { + format: 'dd-MM-yyyy HH:mm aaaaa' + } + fix.detectChanges(); + + grid.openAdvancedFilteringDialog(); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 4); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + + const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) + .injector.get(IgxDateTimeEditorDirective); + expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseDateColumn.pipeArgs.format); + expect(dateTimeEditor.displayFormat.normalize('NFKC')).toMatch(releaseDateColumn.pipeArgs.format); + })); + + it('Time: Should set editorOptions.dateTimeFormat prop as inputFormat for the filter value editor', fakeAsync(() => { + const releaseTimeColumn = grid.getColumnByName('ReleaseTime'); + releaseTimeColumn.editorOptions = { + dateTimeFormat: 'hh:mm' + } + fix.detectChanges(); + + grid.openAdvancedFilteringDialog(); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 6); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + + const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) + .injector.get(IgxDateTimeEditorDirective); + expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseTimeColumn.editorOptions.dateTimeFormat); + })); + + it('Time: Should set pipeArgs.format as inputFormat for the filter editor if numeric and editorOptions.dateTimeFormat not set', fakeAsync(() => { + const releaseTimeColumn = grid.getColumnByName('ReleaseTime'); + releaseTimeColumn.pipeArgs = { + format: 'hh:mm' + } + fix.detectChanges(); + + grid.openAdvancedFilteringDialog(); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); + fix.detectChanges(); + + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 6); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); + + const dateTimeEditor = fix.debugElement.query(By.directive(IgxDateTimeEditorDirective)) + .injector.get(IgxDateTimeEditorDirective); + expect(dateTimeEditor.inputFormat.normalize('NFKC')).toMatch(releaseTimeColumn.pipeArgs.format); + })); it('should handle advanced filtering correctly when grid columns and data are dynamically changed', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridAdvancedFilteringDynamicColumnsComponent); From 2efdde6d77d92ace9521a3c0aacafc4d682cdf7f Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 15 Oct 2024 14:48:03 +0300 Subject: [PATCH 115/147] feat(query-builder): disable commit if nested query has no return fields --- .../query-builder/query-builder-tree.component.html | 1 + .../lib/query-builder/query-builder-tree.component.ts | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index b4c80a70f7d..e712bd194d0 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -455,6 +455,7 @@
[overlaySettings]="returnFieldSelectOverlaySettings" searchPlaceholder="{{ this.resourceStrings.igx_query_builder_search }}" [style.display]="isInEditMode() ? 'block' : 'none'" + [required]="true" >
q.isInEditMode())[0]; return this.selectedField && this.selectedCondition && ( - ((!!this.searchValue.value || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery)) || - (innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined) || + ( + (!!this.searchValue.value || (!!this.searchValueTemplate && !!this._editedExpression.expression.searchVal)) && + !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) + ) || + ( + innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined && innerQuery.selectedReturnFields.length > 0 + ) || this.selectedField.filters.condition(this.selectedCondition).isUnary ); } From ee4757b94cc4a6f068c0d70de9373986bf52766c Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 15 Oct 2024 15:39:05 +0300 Subject: [PATCH 116/147] chore(*): remove required from return fields combo --- .../src/lib/query-builder/query-builder-tree.component.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index e712bd194d0..27d62ddb1af 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -11,7 +11,7 @@ class="igx-query-builder__main" (scroll)="onExpressionsScrolled()" > - +
- + {{ getConditionFriendlyName( - expressionItem.expression.condition.name + expressionItem.expression.condition?.name ) }} @@ -106,35 +106,59 @@
- {{ - isDate(expressionItem.expression.searchVal) - ? getFormatter(expressionItem.expression.fieldName) - ? (expressionItem.expression.searchVal - | fieldFormatter - : getFormatter( - expressionItem.expression.fieldName - ) - : undefined) - : (expressionItem.expression.searchVal - | date - : getFormat( - expressionItem.expression.fieldName - ) - : undefined - : this.locale) - : expressionItem.expression.searchVal - }} + @if(isDate(expressionItem.expression.searchVal)) { + @if(getFormatter(expressionItem.expression.fieldName)) { + {{ + expressionItem.expression.searchVal + | fieldFormatter + : getFormatter( + expressionItem.expression.fieldName + ) + : undefined + }} + } @else { + {{ + expressionItem.expression.searchVal + | date + : getFormat( + expressionItem.expression.fieldName + ) + : undefined + : this.locale + }} + } + } @else { + @if(getFormatter(expressionItem.expression.fieldName)) { + {{ + expressionItem.expression.searchVal + | fieldFormatter + : getFormatter(expressionItem.expression.fieldName) + : (expressionItem.expression.conditionName || expressionItem.expression.condition?.name) + }} + } @else { + {{ expressionItem.expression.searchVal }} + } + }
- {{ - expressionItem.expression.searchTree ? - expressionItem.expression.searchTree.returnFields.join(', ') : - expressionItem.expression.condition.isUnary ? - getConditionFriendlyName(expressionItem.expression.condition.name) : + @if(expressionItem.expression.searchTree){ + {{expressionItem.expression.searchTree.returnFields.join(', ')}} + } @else if (expressionItem.expression.condition.isUnary) { + {{getConditionFriendlyName(expressionItem.expression.condition?.name)}} + } @else { + @if(getFormatter(expressionItem.expression.fieldName)) { + {{ expressionItem.expression.searchVal - }} + | fieldFormatter + : getFormatter(expressionItem.expression.fieldName) + : (expressionItem.expression.conditionName || expressionItem.expression.condition?.name) + }} + } @else { + {{ expressionItem.expression.searchVal }} + } + }
el.field === field).formatter; + return this.fields.find(el => el.field === field)?.formatter; } /** diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index a2e51b7ff49..353cabb1969 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -1,5 +1,5 @@ import { waitForAsync, TestBed, ComponentFixture, fakeAsync, tick, flush } from '@angular/core/testing'; -import { FilteringExpressionsTree, FilteringLogic, IExpressionTree, IgxChipComponent, IgxDateFilteringOperand, IgxNumberFilteringOperand, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxQueryBuilderSearchValueTemplateDirective, IgxStringFilteringOperand } from 'igniteui-angular'; +import { FilteringExpressionsTree, FilteringLogic, IExpressionTree, IgxChipComponent, IgxComboComponent, IgxDateFilteringOperand, IgxNumberFilteringOperand, IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxQueryBuilderSearchValueTemplateDirective, IgxStringFilteringOperand } from 'igniteui-angular'; import { configureTestSuite } from '../test-utils/configure-suite'; import { Component, OnInit, ViewChild } from '@angular/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -8,6 +8,7 @@ import { ControlsFunction } from '../test-utils/controls-functions.spec'; import { QueryBuilderFunctions, QueryBuilderConstants, SampleEntities } from './query-builder-functions.spec'; import { UIInteractions } from '../test-utils/ui-interactions.spec'; import { FormsModule } from '@angular/forms'; +import { NgTemplateOutlet } from '@angular/common'; describe('IgxQueryBuilder', () => { configureTestSuite(); @@ -20,6 +21,7 @@ describe('IgxQueryBuilder', () => { IgxQueryBuilderComponent, IgxQueryBuilderSampleTestComponent, IgxQueryBuilderCustomTemplateSampleTestComponent, + IgxComboComponent ] }).compileComponents(); })); @@ -95,84 +97,6 @@ describe('IgxQueryBuilder', () => { ControlsFunction.verifyButtonIsDisabled(button as HTMLElement, false); } }); - - it('Should render custom header properly.', () => { - const fixture = TestBed.createComponent(IgxQueryBuilderCustomTemplateSampleTestComponent); - fixture.detectChanges(); - - expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fixture)).toBe(' Custom Title '); - expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fixture)).toBeNull(); - expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fixture)).toBeNull(); - - fixture.componentInstance.showLegend = true; - fixture.detectChanges(); - expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fixture).textContent).toBe('and'); - expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fixture).textContent).toBe('or'); - }); - - it('Should render custom input template properly.', fakeAsync(() => { - const fixture = TestBed.createComponent(IgxQueryBuilderCustomTemplateSampleTestComponent); - fixture.detectChanges(); - - //Select chip - QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fixture, [0]); - tick(200); - fixture.detectChanges(); - - //Open edit mode - QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fixture, [0], 'edit'); - tick(200); - fixture.detectChanges(); - - const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fixture, false, 0); - const input = editModeContainer.querySelector('input.custom-class') as HTMLInputElement; - const selectedField = editModeContainer.querySelector('p.selectedField') as HTMLInputElement; - const selectedCondition = editModeContainer.querySelector('p.selectedCondition') as HTMLInputElement; - - expect(input).toBeDefined(); - expect(input.value).toBe('3'); - expect(selectedField).toBeDefined(); - expect(selectedField.innerText).toBe('OrderId'); - expect(selectedCondition).toBeDefined(); - expect(selectedCondition.innerText).toBe('greaterThan'); - - //Edit input value - UIInteractions.clickAndSendInputElementValue(input, '5'); - tick(100); - fixture.detectChanges(); - - // Commit the populated expression. - QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fixture); - tick(100); - fixture.detectChanges(); - - //Verify that expressionTree is correct - const exprTree = JSON.stringify(fixture.componentInstance.queryBuilder.expressionTree, null, 2); - expect(exprTree).toBe(`{ - "filteringOperands": [ - { - "fieldName": "OrderId", - "condition": { - "name": "greaterThan", - "isUnary": false, - "iconName": "filter_greater_than" - }, - "conditionName": "greaterThan", - "searchVal": 5, - "searchTree": null, - "ignoreCase": true - } - ], - "operator": 0, - "entity": "Orders", - "returnFields": [ - "OrderId", - "OrderName", - "OrderDate", - "Delivered" - ] -}`); - })); }); describe('Interactions', () => { @@ -777,7 +701,7 @@ describe('IgxQueryBuilder', () => { }, "conditionName": null, "ignoreCase": true, - "searchVal": 5, + "searchVal": "5", "searchTree": null } ], @@ -830,7 +754,7 @@ describe('IgxQueryBuilder', () => { }, "conditionName": null, "ignoreCase": true, - "searchVal": 5, + "searchVal": "5", "searchTree": null } ], @@ -2150,6 +2074,135 @@ describe('IgxQueryBuilder', () => { })); }); + describe('Templates', () => { + let fixture: ComponentFixture; + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(IgxQueryBuilderCustomTemplateSampleTestComponent); + fixture.detectChanges(); + queryBuilder = fixture.componentInstance.queryBuilder; + })); + + it('Should render custom header properly.', () => { + expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fixture)).toBe(' Custom Title '); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fixture)).toBeNull(); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fixture)).toBeNull(); + + fixture.componentInstance.showLegend = true; + fixture.detectChanges(); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fixture).textContent).toBe('and'); + expect(QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fixture).textContent).toBe('or'); + }); + + it('Should render custom input template properly.', fakeAsync(() => { + //Select chip + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fixture, [0]); + tick(200); + fixture.detectChanges(); + + //Open edit mode + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChipIcon(fixture, [0], 'edit'); + tick(200); + fixture.detectChanges(); + + const editModeContainer = QueryBuilderFunctions.getQueryBuilderEditModeContainer(fixture, false); + const input = editModeContainer.querySelector('input.custom-class') as HTMLInputElement; + const selectedField = editModeContainer.querySelector('p.selectedField') as HTMLInputElement; + const selectedCondition = editModeContainer.querySelector('p.selectedCondition') as HTMLInputElement; + + expect(input).toBeDefined(); + expect(input.value).toBe('3'); + expect(selectedField).toBeDefined(); + expect(selectedField.innerText).toBe('OrderId'); + expect(selectedCondition).toBeDefined(); + expect(selectedCondition.innerText).toBe('greaterThan'); + + //Edit input value + UIInteractions.clickAndSendInputElementValue(input, '5'); + tick(100); + fixture.detectChanges(); + + // Commit the populated expression. + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fixture); + tick(100); + fixture.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fixture.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderId", + "condition": { + "name": "greaterThan", + "isUnary": false, + "iconName": "filter_greater_than" + }, + "conditionName": "greaterThan", + "searchVal": "5", + "searchTree": null, + "ignoreCase": true + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate", + "Delivered" + ] +}`); + })); + + it('Should apply field formatter properly.', fakeAsync(() => { + // Add new expression + const btn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fixture, 0)[0] as HTMLElement; + btn.click(); + fixture.detectChanges(); + + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fixture, 0); // Select 'OrderId' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fixture, 0); // Select 'Equals' operator. + + // Verify combo template is displayed + let editModeContainer = Array.from(QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fixture).querySelectorAll('.igx-filter-tree__inputs'))[1]; + let combo = editModeContainer.querySelector('.igx-combo'); + expect(combo).toBeDefined(); + + // Open the combo + (combo.querySelector('igx-input-group') as HTMLElement).click(); + tick(); + fixture.detectChanges(); + // Select item + const outlet = Array.from(document.querySelectorAll(`.igx-drop-down__list-scroll`)) + .filter(item => (item as HTMLElement).checkVisibility())[0] as HTMLElement; + + const comboItem = outlet.querySelectorAll(`.igx-drop-down__item`)[0] as HTMLElement; + comboItem.click(); + tick(); + fixture.detectChanges(); + + // Commit the expression + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fixture); + fixture.detectChanges(); + // Verify chips + QueryBuilderFunctions.verifyExpressionChipContent(fixture, [0], 'OrderId', 'Greater Than', '3'); + QueryBuilderFunctions.verifyExpressionChipContent(fixture, [1], 'OrderId', 'Equals', '0'); + + // Enter edit mode + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fixture, [1], true); + tick(50); + fixture.detectChanges(); + // Verify inputs values + editModeContainer = Array.from(QueryBuilderFunctions.getQueryBuilderExpressionsContainer(fixture).querySelectorAll('.igx-filter-tree__inputs'))[1]; + const selects = Array.from(editModeContainer.querySelectorAll('igx-select')); + combo = editModeContainer.querySelector('.igx-combo'); + expect(selects[0].querySelector('input').value).toBe('OrderId'); + expect(selects[1].querySelector('input').value).toBe('Equals'); + expect(combo.querySelector('input').value).toBe('A'); + })); + }); + describe('Localization', () => { it('Should correctly change resource strings for Query Builder.', fakeAsync(() => { queryBuilder.resourceStrings = Object.assign({}, queryBuilder.resourceStrings, { @@ -2316,9 +2369,17 @@ export class IgxQueryBuilderSampleTestComponent implements OnInit { let-selectedField = "selectedField" let-selectedCondition = "selectedCondition" let-defaultSearchValueTemplate = "defaultSearchValueTemplate"> - -

{{selectedField.field}}

-

{{selectedCondition}}

+ @if (selectedField?.field === 'OrderId' && selectedCondition === 'greaterThan'){ + +

{{selectedField.field}}

+

{{selectedCondition}}

+ } @else if (selectedField?.field === 'OrderId' && selectedCondition === 'equals') { + + + } @else { + + } `, @@ -2327,6 +2388,8 @@ export class IgxQueryBuilderSampleTestComponent implements OnInit { IgxQueryBuilderComponent, IgxQueryBuilderHeaderComponent, IgxQueryBuilderSearchValueTemplateDirective, + IgxComboComponent, + NgTemplateOutlet, FormsModule ] }) @@ -2337,6 +2400,7 @@ export class IgxQueryBuilderCustomTemplateSampleTestComponent implements OnInit public entities: Array; public showLegend = false; public expressionTree: IExpressionTree; + public comboData: any[]; public ngOnInit(): void { @@ -2352,5 +2416,17 @@ export class IgxQueryBuilderCustomTemplateSampleTestComponent implements OnInit }); this.expressionTree = tree; + + this.comboData = [ + { id: 0, field: 'A' }, + { id: 1, field: 'B' } + ]; + } + + public handleChange(ev, selectedField, searchVal) { + if (selectedField.field === 'OrderId') { + searchVal.value = ev.newValue[0]; + selectedField.formatter = (value: any, rowData: any) => rowData === 'equals' ? value[0].id : value; + } } } diff --git a/src/app/query-builder/query-builder.sample.html b/src/app/query-builder/query-builder.sample.html index 86392e47a72..072b16b56fb 100644 --- a/src/app/query-builder/query-builder.sample.html +++ b/src/app/query-builder/query-builder.sample.html @@ -2,26 +2,28 @@
- - + + + + } @else { + } - --> + +
+ + + +
+
{{ this.queryResult }}
diff --git a/src/app/query-builder/query-builder.sample.scss b/src/app/query-builder/query-builder.sample.scss index ef0203d52eb..4cf7ec02d95 100644 --- a/src/app/query-builder/query-builder.sample.scss +++ b/src/app/query-builder/query-builder.sample.scss @@ -13,6 +13,10 @@ margin-top: 24px; display: flex; flex-wrap: wrap; + + button { + margin-right: 10px; + } } .output-area{ diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index 05d03665a1c..a44826975f4 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -161,6 +161,18 @@ export class QueryBuilderComponent implements OnInit { return tree ? JSON.stringify(tree, null, 2) : 'Please add an expression!'; } + public canCommitExpressionTree() { + console.log(this.queryBuilder.canCommit()); + } + + public commitExpressionTree() { + this.queryBuilder.commit(); + } + + public discardExpressionTree() { + this.queryBuilder.discard(); + } + // public handleChange(ev, selectedField, searchVal) { // if (selectedField.field === 'Id') { // searchVal.value = ev.newValue[0]; From d4314a5a95bd5fa1d6203b478c2132837eaf75ad Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Tue, 29 Oct 2024 16:33:43 +0200 Subject: [PATCH 123/147] fix(query-builder): sample custom search template formatter --- src/app/query-builder/query-builder.sample.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/query-builder/query-builder.sample.ts b/src/app/query-builder/query-builder.sample.ts index a44826975f4..43242cce8fa 100644 --- a/src/app/query-builder/query-builder.sample.ts +++ b/src/app/query-builder/query-builder.sample.ts @@ -47,7 +47,7 @@ export class QueryBuilderComponent implements OnInit { public ngOnInit(): void { this.fieldsEntityA = [ - { field: 'Id', dataType: 'number', formatter: (value: any, rowData: any) => rowData === 'equals' ? value[0].id : value }, + { field: 'Id', dataType: 'number', formatter: (value: any, rowData: any) => rowData === 'equals' ? `${value.map((v: { id: any; }) => v.id)}` : value }, { field: 'Name', dataType: 'string' }, { field: 'Validated', dataType: 'boolean' }, { field: 'Date created', dataType: 'date' }, From 0056177c395b99d77ddf9d7aa0ddecfe7acde87d Mon Sep 17 00:00:00 2001 From: igdmdimitrov <49060557+igdmdimitrov@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:21:54 +0200 Subject: [PATCH 124/147] feat(query-builder): save and restore inner query state on commit/discard (#14916) * feat(query-builder): save and restore inner query state --------- Co-authored-by: INFRAGISTICS\IPetrov --- .../query-builder-tree.component.html | 2 +- .../query-builder-tree.component.ts | 108 ++++++++++-------- .../query-builder.component.spec.ts | 35 ++++++ 3 files changed, 98 insertions(+), 47 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index e91f0eb6afa..b05c8d94786 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -411,7 +411,7 @@
[entities]="entities" [queryBuilder]="this.queryBuilder" [parentExpression]="expressionItem" - [expressionTree]="expressionItem.expression.searchTree" + [expressionTree]="expressionItem.inEditMode ? (innerQueryNewExpressionTree ?? getExpressionTreeCopy(expressionItem.expression.searchTree, true)) : expressionItem.expression.searchTree" (inEditModeChange)="onInEditModeChanged($event)" [searchValueTemplate]="searchValueTemplate"> diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index dabb4029547..4d7b8edd283 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -228,15 +228,13 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ @Input() public set expressionTree(expressionTree: IExpressionTree) { - if (JSON.stringify(expressionTree) !== JSON.stringify(this._expressionTree)) { - this._expressionTree = expressionTree; - if (!expressionTree) { - this._selectedEntity = null; - this._selectedReturnFields = []; - } - - this.init(); + this._expressionTree = expressionTree; + if (!expressionTree) { + this._selectedEntity = null; + this._selectedReturnFields = []; } + + this.init(); } /** @@ -280,10 +278,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { /** * Event fired as the expression tree is changed. - * - * ```html - * - * ``` */ @Output() public expressionTreeChange = new EventEmitter(); @@ -390,6 +384,11 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { @ViewChildren(IgxQueryBuilderTreeComponent) private innerQueries: QueryList; + /** + * @hidden @internal + */ + public innerQueryNewExpressionTree: IExpressionTree; + /** * @hidden @internal */ @@ -468,7 +467,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { private destroy$ = new Subject(); private _parentExpression: ExpressionOperandItem; - private _initialExpressionTree: IExpressionTree; private _selectedEntity: EntityType; private _selectedReturnFields: string | string[]; private _selectedField: FieldType; @@ -541,6 +539,13 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.destroy$.complete(); } + /** + * @hidden @internal + */ + public set selectedEntity(value: string) { + this._selectedEntity = this.entities?.find(el => el.name === value); + } + /** * @hidden @internal */ @@ -586,16 +591,13 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedEntity.fields = []; } this.fields = this._entityNewValue ? this._entityNewValue.fields : []; - this._selectedReturnFields = this._entityNewValue.fields?.map(f => f.field); if (this._expressionTree) { - this._expressionTree.entity = this._selectedEntity.name; - this._expressionTree.returnFields = []; - this._expressionTree.filteringOperands = []; - this._editedExpression = null; - this.expressionTreeChange.emit(this._expressionTree); + if (!this.parentExpression) { + this.expressionTreeChange.emit(this._expressionTree); + } this.addAndGroup(); } @@ -608,21 +610,21 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.entitySelect.close(); this._entityNewValue = null; + this.innerQueryNewExpressionTree = null; } /** * @hidden @internal */ public set selectedReturnFields(value: string[]) { - const oldValue = this._selectedReturnFields; - - if (this._selectedReturnFields !== value && oldValue !== value) { + if (this._selectedReturnFields !== value) { this._selectedReturnFields = value; - if (this._expressionTree) { + if (this._expressionTree && !this.parentExpression) { this._expressionTree.returnFields = value; this.expressionTreeChange.emit(this._expressionTree); } + } } @@ -747,20 +749,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0] if (innerQuery && this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) { - if (!this._editedExpression.expression.searchTree && innerQuery.expressionTree) { - this._editedExpression.expression.searchTree = new FilteringExpressionsTree(innerQuery.expressionTree.operator); - } - - if (this._editedExpression.expression.searchTree) { - this._editedExpression.expression.searchTree.entity = innerQuery.selectedEntity.name; - this._editedExpression.expression.searchTree.returnFields = innerQuery.selectedReturnFields; - this._editedExpression.expression.searchTree.filteringOperands = innerQuery.expressionTree?.filteringOperands; - this._editedExpression.expression.searchTree.operator = innerQuery.expressionTree?.operator; - this._editedExpression.expression.searchTree.fieldName = innerQuery.expressionTree?.fieldName; - } + this._editedExpression.expression.searchTree = this.getExpressionTreeCopy(innerQuery.expressionTree); + this._editedExpression.expression.searchTree.returnFields = innerQuery.selectedReturnFields; } else { this._editedExpression.expression.searchTree = null; } + this.innerQueryNewExpressionTree = null; if (this.selectedField.filters.condition(this.selectedCondition)?.isUnary) { this._editedExpression.expression.searchVal = null; @@ -771,7 +765,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } this._expressionTree = this.createExpressionTreeFromGroupItem(this.rootGroup, this.selectedEntity?.name, this.selectedReturnFields); - this.expressionTreeChange.emit(this._expressionTree); + if (!this.parentExpression) { + this.expressionTreeChange.emit(this._expressionTree); + } } /** @@ -791,11 +787,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (this.innerQueries) { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; if (innerQuery) { - innerQuery.expressionTree = this._initialExpressionTree; - if (innerQuery._editedExpression) { innerQuery.cancelOperandEdit(); } + + innerQuery.expressionTree = this.getExpressionTreeCopy(this._editedExpression.expression.searchTree); + this.innerQueryNewExpressionTree = null; } } @@ -815,6 +812,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public operandCanBeCommitted(): boolean { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; + return this.selectedField && this.selectedCondition && ( ( @@ -928,7 +926,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._editedExpression.inEditMode = false; } - if (this.parentExpression) { + if (this.parentExpression && !this.parentExpression.inEditMode) { this.inEditModeChange.emit(this.parentExpression); } @@ -946,12 +944,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { expressionItem.inEditMode = true; this._editedExpression = expressionItem; - if (expressionItem.expression.searchTree) { - this._initialExpressionTree = expressionItem.expression.searchTree; - } else { - this._initialExpressionTree = null; - } - this.cdr.detectChanges(); this.entitySelectOverlaySettings.target = this.entitySelect.element; @@ -1269,6 +1261,28 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } + public getExpressionTreeCopy(expressionTree: IExpressionTree, shouldAssignInnerQueryExprTree?: boolean): IExpressionTree { + if (!expressionTree) { + return null; + } + + const exprTreeCopy = + { + filteringOperands: [], + operator: expressionTree.operator, + fieldName: expressionTree.fieldName, + entity: expressionTree.entity, + returnFields: expressionTree.returnFields + }; + expressionTree.filteringOperands.forEach(o => o instanceof FilteringExpressionsTree ? exprTreeCopy.filteringOperands.push(this.getExpressionTreeCopy(o)) : exprTreeCopy.filteringOperands.push(o)); + + if (!this.innerQueryNewExpressionTree && shouldAssignInnerQueryExprTree) { + this.innerQueryNewExpressionTree = exprTreeCopy; + } + + return exprTreeCopy; + } + public onSelectAllClicked(_event) { if ( (this._selectedReturnFields.length > 0 && this._selectedReturnFields.length < this._selectedEntity.fields.length) || @@ -1387,7 +1401,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { condition: filteringExpr.condition, conditionName: filteringExpr.condition.name, searchVal: filteringExpr.searchVal, - searchTree: filteringExpr.searchTree, // this.createExpressionGroupItem(filteringExpr.searchTree, groupItem), + searchTree: filteringExpr.searchTree, ignoreCase: filteringExpr.ignoreCase }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); @@ -1504,7 +1518,9 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.deleteItem(expressionItem.parent); } - this.expressionTreeChange.emit(this._expressionTree); + if (!this.parentExpression) { + this.expressionTreeChange.emit(this._expressionTree); + } } private createGroup(operator: FilteringLogic) { diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index b83e1a38cb2..a4f261b80f3 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -1989,6 +1989,38 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); })); + it('Should not make bug where existing inner query is leaking to a newly created one', fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; + + // Add new 'expression'. + const buttonsContainer = Array.from(group.querySelectorAll('.igx-filter-tree__buttons'))[0]; + const buttons = Array.from(buttonsContainer.querySelectorAll('button')); + (buttons[0] as HTMLElement).click(); + tick(); + fix.detectChanges(); + + // Add condition with 'in' operator to open inner query + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderName' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'Contains' operator. + tick(100); + fix.detectChanges(); + + //New empty inner query should be displayed + const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; + const bodyElement = queryBuilderElement.children[1].children[0]; + const actionArea = bodyElement.children[0].querySelector('.igx-query-builder__root-actions'); + expect(actionArea).toBeDefined('action area is missing'); + expect(actionArea).not.toBeNull('action area is missing'); + expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); + expect(bodyElement.children[0].children[1].children[6]).toHaveClass('igx-query-builder-tree'); + expect(bodyElement.children[0].children[1].children[6].children.length).toEqual(3); + const tree = bodyElement.children[0].children[1].children[6].querySelector('.igx-filter-tree__expression'); + expect(tree).toBeNull(); + })); + it('canCommit should return the correct validity state of currently edited condition.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); @@ -2150,6 +2182,9 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); tick(50); fix.detectChanges(); + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true, 1); + tick(50); + fix.detectChanges(); expect(queryBuilder.canCommit()).toBeTrue(); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0, 1); expect(queryBuilder.canCommit()).toBeFalse(); From 138243b7a705aefb73c258976764f1b9702b1c40 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Wed, 30 Oct 2024 15:31:51 +0200 Subject: [PATCH 125/147] refactor(query-builder): simple select from --- .../query-builder-functions.spec.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index 210ab0bf62d..2ef82e35ce1 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -530,10 +530,10 @@ export class QueryBuilderFunctions { public static verifyEditModeQueryExpressionInputStates(fix, entitySelectEnabled: boolean, fieldComboEnabled: boolean, - columnSelectEnabled: boolean, - operatorSelectEnabled: boolean, - valueInputEnabled: boolean, - commitButtonEnabled: boolean, + columnSelectEnabled: boolean = null, + operatorSelectEnabled: boolean = null, + valueInputEnabled: boolean = null, + commitButtonEnabled: boolean = null, level = 0) { // Verify the entity select state. const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('igx-input-group'); @@ -544,7 +544,9 @@ export class QueryBuilderFunctions { expect(!fieldInputGroup.classList.contains('igx-input-group--disabled')).toBe(fieldComboEnabled, 'incorrect fields combo state'); - QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, columnSelectEnabled, operatorSelectEnabled, valueInputEnabled, commitButtonEnabled, level); + if(columnSelectEnabled || operatorSelectEnabled || valueInputEnabled || commitButtonEnabled){ + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, columnSelectEnabled, operatorSelectEnabled, valueInputEnabled, commitButtonEnabled, level); + } }; public static verifyEditModeExpressionInputStates(fix, @@ -581,15 +583,18 @@ export class QueryBuilderFunctions { public static verifyQueryEditModeExpressionInputValues(fix, entityText: string, fieldsText: string, - columnText: string, - operatorText: string, - valueText: string, + columnText: string = null, + operatorText: string = null, + valueText: string = null, level = 0) { const entityInput = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('input'); const fieldInput = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fix, level).querySelector('input'); - QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, columnText, operatorText, valueText, level); expect(entityInput.value).toBe(entityText); expect(fieldInput.value).toBe(fieldsText); + + if(columnText || operatorText || valueText){ + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, columnText, operatorText, valueText, level); + } }; public static verifyEditModeExpressionInputValues(fix, From a2fe0721845f8865cbcdad4a44b2c2afa9a88735 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Wed, 30 Oct 2024 15:32:10 +0200 Subject: [PATCH 126/147] refactor(query-builder): simple select from --- .../query-builder-tree.component.html | 111 +++++----- .../query-builder-tree.component.ts | 58 ++++-- .../query-builder.component.spec.ts | 195 ++++++++++++++---- 3 files changed, 252 insertions(+), 112 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index b05c8d94786..b3c8420f276 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -6,12 +6,66 @@ + +
+ + + + {{entity.name}} + + + + + +
+ + +
+ {{ this.resourceStrings.igx_query_builder_select_all }} +
+
+
+ +
+
+
+
- + +
-
(click)="onGroupClick(expressionItem)" >
-
-
- - - - - {{entity.name}} - - - - - -
- - -
- {{ this.resourceStrings.igx_query_builder_select_all }} -
-
-
- -
-
-
- +
f.field); if (this._expressionTree) { + this._expressionTree.entity = this._entityNewValue.name; + this._expressionTree.returnFields = []; + this._expressionTree.filteringOperands = []; + this._editedExpression = null; if (!this.parentExpression) { this.expressionTreeChange.emit(this._expressionTree); } - this.addAndGroup(); + this.rootGroup = null; + this.currentGroup = this.rootGroup; } this._selectedField = null; @@ -611,6 +620,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._entityNewValue = null; this.innerQueryNewExpressionTree = null; + + this.initExpressionTree(this._selectedEntity.name, this.selectedReturnFields); } /** @@ -1294,6 +1305,20 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } + public onReturnFieldSelectChanging(event: IComboSelectionChangingEventArgs) { + this.initExpressionTree(this.selectedEntity.name, event.newSelection.map(item => item.field)) + } + + public initExpressionTree(selectedEntityName: string, selectedReturnFields: string[]) { + if (!this._expressionTree) { + this._expressionTree = this.createExpressionTreeFromGroupItem(new ExpressionGroupItem(FilteringLogic.And, this.rootGroup), selectedEntityName, selectedReturnFields); + } + + if (!this.parentExpression) { + this.expressionTreeChange.emit(this._expressionTree); + } + } + public getSearchValueTemplateContext(defaultSearchValueTemplate): any { const ctx = { $implicit: this.searchValue, @@ -1405,26 +1430,29 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { ignoreCase: filteringExpr.ignoreCase }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); - if (expressionTree.entity) { - entityName = expressionTree.entity; - } - const entity = this.entities?.find(el => el.name === entityName); - if (entity) { - this.fields = entity.fields; - } const field = this.fields?.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field?.label || field?.header || field?.field; if (this._expandedExpressions.filter(e => e.searchTree == operandItem.expression.searchTree).length > 0) { operandItem.expanded = true; } groupItem.children.push(operandItem); - this._selectedEntity = this.entities?.find(el => el.name === entityName); - this._selectedReturnFields = - !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') - ? this.fields?.map(f => f.field) - : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } } + + + if (expressionTree.entity) { + entityName = expressionTree.entity; + } + const entity = this.entities?.find(el => el.name === entityName); + if (entity) { + this.fields = entity.fields; + } + + this._selectedEntity = this.entities?.find(el => el.name === entityName); + this._selectedReturnFields = + !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') + ? this.fields?.map(f => f.field) + : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } return groupItem; } @@ -1499,7 +1527,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (!expressionItem.parent) { this.rootGroup = null; this.currentGroup = null; - this._expressionTree = null; + //this._expressionTree = null; return; } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index a4f261b80f3..ccc8d4a05c6 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -50,35 +50,34 @@ describe('IgxQueryBuilder', () => { const bodyElement = queryTreeElement.children[0]; expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); expect(bodyElement.children.length).toEqual(2); - expect(bodyElement.children[0]).toHaveClass('igx-query-builder__root'); - const actionArea = bodyElement.children[0].querySelector('.igx-query-builder__root-actions'); - // initial add "'and'/'or' group " buttons should be displayed - expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); - // empty filtering tree message should be displayed - expect(bodyElement.children[0].children[1]).toHaveClass('igx-filter-empty'); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', ''); }); - it('Should render Query Builder with initially set expression tree properly.', () => { + it('Should render Query Builder with initially set expression tree properly.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); + tick(100); + fix.detectChanges(); const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; const bodyElement = queryTreeElement.children[0]; expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); - expect(bodyElement.children.length).toEqual(2); + expect(bodyElement.children.length).toEqual(3); // initial add "'and'/'or' group " buttons and empty filtering tree message should NOT be displayed expect(bodyElement.querySelectorAll(':scope > button').length).toEqual(0); - expect(bodyElement.children[0]).toHaveClass('igx-filter-tree'); + expect(bodyElement.children[1]).toHaveClass('igx-filter-tree'); // Verify the operator line of the root group is an 'And' line. QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); // all inputs should be displayed correctly - const queryTreeExpressionContainer = bodyElement.children[0].children[1]; + const selectFromContainer = bodyElement.children[0]; + expect(selectFromContainer).toHaveClass('igx-filter-tree__inputs'); + expect(selectFromContainer.children[0].tagName).toEqual('IGX-SELECT'); + expect(selectFromContainer.children[1].tagName).toEqual('IGX-COMBO'); + const queryTreeExpressionContainer = bodyElement.children[1].children[1]; expect(queryTreeExpressionContainer).toHaveClass('igx-filter-tree__expression'); - expect(queryTreeExpressionContainer.children[0]).toHaveClass('igx-filter-tree__inputs'); - expect(queryTreeExpressionContainer.children[0].children[0].tagName).toEqual('IGX-SELECT'); - expect(queryTreeExpressionContainer.children[0].children[1].tagName).toEqual('IGX-COMBO'); const expressionItems = queryTreeExpressionContainer.querySelectorAll(':scope > .igx-filter-tree__expression-item'); expect(expressionItems.length).toEqual(queryBuilder.expressionTree.filteringOperands.length); @@ -96,11 +95,15 @@ describe('IgxQueryBuilder', () => { for (const button of buttons) { ControlsFunction.verifyButtonIsDisabled(button as HTMLElement, false); } - }); + })); }); describe('Interactions', () => { it('Should correctly initialize a newly added \'And\' group.', fakeAsync(() => { + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + // Click the initial 'Add And Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); @@ -113,10 +116,10 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); // Verify the enabled/disabled state of each input of the expression in edit mode. - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false); // Verify the edit inputs are empty. - QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', '', '', '', ''); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); // Verify adding buttons are disabled. const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); @@ -126,6 +129,10 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => { + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); @@ -1169,6 +1176,7 @@ describe('IgxQueryBuilder', () => { fix.detectChanges(); tick(100); fix.detectChanges(); + // Verify actions container is not visible. (This container contains the 'edit' and the 'add' buttons.) expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) .toBeNull('actions container is visible'); @@ -1270,6 +1278,10 @@ describe('IgxQueryBuilder', () => { it('Should select/deselect all child conditions and groups when clicking a group\'s operator line.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + const childGroup = new FilteringExpressionsTree(FilteringLogic.Or, undefined, undefined); childGroup.filteringOperands.push({ fieldName: 'OrderNameName', @@ -1479,6 +1491,10 @@ describe('IgxQueryBuilder', () => { tick(100); fix.detectChanges(); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + // Verify all inputs QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); })); @@ -1529,6 +1545,8 @@ describe('IgxQueryBuilder', () => { it(`Should display 'expand'/'collapse' button properly.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); + tick(100); + fix.detectChanges(); const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; // Nested query tree should have expand collapse button @@ -1718,6 +1736,8 @@ describe('IgxQueryBuilder', () => { it(`Should toggle the nested query on 'expand'/'collapse' button click.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); + tick(100); + fix.detectChanges(); expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); @@ -1741,7 +1761,9 @@ describe('IgxQueryBuilder', () => { it('Should create an "and"/"or" group on context menu button click and delete conditions on "delete filters" click.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + // Verify group types initially QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); @@ -1788,7 +1810,9 @@ describe('IgxQueryBuilder', () => { it('Ungroup button of the root group\'s context menu should be disabled.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + // Click root group's operator line. const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; rootOperatorLine.click(); @@ -1806,7 +1830,9 @@ describe('IgxQueryBuilder', () => { it('Should remove a group from the expr tree when clicking "delete" from the context menu.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); //create group @@ -1833,7 +1859,9 @@ describe('IgxQueryBuilder', () => { it('Should be able to open edit mode when condition is selected, close the edited condition on "close" button click and not commit it.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + //Select chip QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); tick(200); @@ -1872,7 +1900,9 @@ describe('IgxQueryBuilder', () => { it('Selecting/deselecting multiple conditions should display/hide the (create group)/(delete filters) context menu properly.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); //select 1 @@ -1911,7 +1941,9 @@ describe('IgxQueryBuilder', () => { it(`Should show/hide the group's context menu when clicking its operator line and should close the context menu when clicking its close button.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); @@ -1945,7 +1977,9 @@ describe('IgxQueryBuilder', () => { it('Should be able to group, change And/Or and un-group conditions.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); @@ -1992,7 +2026,9 @@ describe('IgxQueryBuilder', () => { it('Should not make bug where existing inner query is leaking to a newly created one', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; // Add new 'expression'. @@ -2046,46 +2082,105 @@ describe('IgxQueryBuilder', () => { })); it('canCommit should return the correct validity state of currently added condition.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + // Verify the Query Builder validity state while adding a condition. + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Entity selected').toBeTrue(); - // Verify the Query Builder validity state while adding a condition. - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the 'Add condition' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Add condition clicked').toBeTrue(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Column selected').toBeFalse(); QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Operator contains selected').toBeFalse(); const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'a'); tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeTrue(); + expect(queryBuilder.canCommit()).withContext('Search value filled').toBeTrue(); // Click on the 'cancel' button const closeButton = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix); UIInteractions.simulateClickEvent(closeButton); tick(100); fix.detectChanges(); + expect(queryBuilder.canCommit()).withContext('Entity remains selected').toBeTrue(); // Verify the Query Builder validity state for UNARY condition. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); + expect(queryBuilder.canCommit()).withContext('Add condition clicked again').toBeTrue(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); + expect(queryBuilder.canCommit()).withContext('Column selected again').toBeFalse(); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); + expect(queryBuilder.canCommit()).withContext('Unary operator selected').toBeTrue(); + })); + + + it('Should be able to commit nested query without where condition.', fakeAsync(() => { QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. + tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeFalse(); - QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); - expect(queryBuilder.canCommit()).toBeFalse(); - QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); - expect(queryBuilder.canCommit()).toBeTrue(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. + + // Enter values in the nested query + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, 1); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, true); // Parent commit button should be enabled + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderId", + "condition": { + "name": "in", + "isUnary": false, + "isNestedQuery": true, + "iconName": "in" + }, + "conditionName": "in", + "ignoreCase": true, + "searchVal": null, + "searchTree": { + "filteringOperands": [], + "operator": 0, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] + } + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate", + "Delivered" + ] +}`); })); }); @@ -2244,14 +2339,18 @@ describe('IgxQueryBuilder', () => { it('Should navigate with Tab/Shift+Tab through entity and fields inputs, chips, their respective delete icons and the operator lines.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyQueryBuilderTabbableElements(fix); })); it('Should navigate with Tab/Shift+Tab through chips" "edit", "cancel" and "adding" buttons, fields of a condition in edit mode.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const chip = fix.debugElement.queryAll(By.directive(IgxChipComponent))[0]; QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chip.nativeElement, 200); @@ -2273,7 +2372,9 @@ describe('IgxQueryBuilder', () => { //!Both Enter and Space should work queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const chip = fix.debugElement.queryAll(By.directive(IgxChipComponent))[0]; QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, false); @@ -2301,7 +2402,9 @@ describe('IgxQueryBuilder', () => { //!Both Enter and Space should work queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const line = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS}`))[0]; const chips = fix.debugElement.queryAll(By.directive(IgxChipComponent)); @@ -2338,7 +2441,9 @@ describe('IgxQueryBuilder', () => { //!Both Enter and Space should work queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + // Verify the there are three chip expressions. QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); @@ -2547,6 +2652,10 @@ describe('IgxQueryBuilder', () => { expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[0].textContent.trim()).toBe('My Cancel'); expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[1].textContent.trim()).toBe('My Confirm'); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. From d724e2ed6f2005945ae35bb07b42329677dba1f7 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Wed, 30 Oct 2024 17:52:13 +0200 Subject: [PATCH 127/147] feat(query-builder): add isAdvancedFiltering method --- .../lib/query-builder/query-builder-tree.component.html | 2 +- .../lib/query-builder/query-builder-tree.component.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index b3c8420f276..bd561a83a11 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -7,7 +7,7 @@ -
+
Date: Wed, 30 Oct 2024 18:09:56 +0200 Subject: [PATCH 128/147] fix(query-builder): now root group is not initialized --- .../src/lib/query-builder/query-builder-tree.component.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 6987674c739..a6d2aa610ca 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1654,8 +1654,10 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.clearSelection(); this.cancelOperandAdd(); this.cancelOperandEdit(); - this.rootGroup = this.createExpressionGroupItem(this.expressionTree); - this.currentGroup = this.rootGroup; + if(this.isAdvancedFiltering()){ + this.rootGroup = this.createExpressionGroupItem(this.expressionTree); + this.currentGroup = this.rootGroup; + } } } From 7cffe44ea71fade0fe7ab1dde78403e469e8a3b6 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 31 Oct 2024 10:05:55 +0200 Subject: [PATCH 129/147] chore(*): additional checks --- .../query-builder-tree.component.ts | 20 ++++++++++++------- .../query-builder/query-builder.component.ts | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 4d7b8edd283..abedccc2b1e 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -926,7 +926,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._editedExpression.inEditMode = false; } - if (this.parentExpression && !this.parentExpression.inEditMode) { + if (this.parentExpression) { this.inEditModeChange.emit(this.parentExpression); } @@ -952,12 +952,18 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.returnFieldSelectOverlaySettings.target = this.selectedReturnFieldsCombo.getEditElement(); this.returnFieldSelectOverlaySettings.excludeFromOutsideClick = [this.selectedReturnFieldsCombo.getEditElement() as HTMLElement]; this.returnFieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); - this.fieldSelectOverlaySettings.target = this.fieldSelect.element; - this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; - this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); - this.conditionSelectOverlaySettings.target = this.conditionSelect.element; - this.conditionSelectOverlaySettings.excludeFromOutsideClick = [this.conditionSelect.element as HTMLElement]; - this.conditionSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + + if (this.fieldSelect) { + this.fieldSelectOverlaySettings.target = this.fieldSelect.element; + this.fieldSelectOverlaySettings.excludeFromOutsideClick = [this.fieldSelect.element as HTMLElement]; + this.fieldSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + } + if (this.conditionSelect) { + this.conditionSelectOverlaySettings.target = this.conditionSelect.element; + this.conditionSelectOverlaySettings.excludeFromOutsideClick = [this.conditionSelect.element as HTMLElement]; + this.conditionSelectOverlaySettings.positionStrategy = new AutoPositionStrategy(); + } + if (!this.selectedField) { this.fieldSelect.input.nativeElement.focus(); diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts index cd2d29791df..4474ebf2895 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts @@ -167,7 +167,7 @@ export class IgxQueryBuilderComponent implements OnDestroy { * Returns whether the expression tree can be committed in the current state. */ public canCommit(): boolean { - return this.queryTree.canCommitCurrentState() === true; + return this.queryTree?.canCommitCurrentState() === true; } /** From 37bbd58e05df502cce85f300a3166c1c43ebf567 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Thu, 31 Oct 2024 12:03:27 +0200 Subject: [PATCH 130/147] fix(query-builder): fixed bug with rootGroup initialization --- .../query-builder-tree.component.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index a6d2aa610ca..0e3148239fe 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -828,7 +828,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public operandCanBeCommitted(): boolean { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; - + return this.selectedField && this.selectedCondition && ( ( @@ -836,7 +836,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) ) || ( - this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery && innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined && innerQuery.selectedReturnFields.length > 0 + this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery && innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined && innerQuery.selectedReturnFields?.length > 0 ) || this.selectedField.filters.condition(this.selectedCondition)?.isUnary ); @@ -1282,7 +1282,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { return null; } - const exprTreeCopy = + const exprTreeCopy = { filteringOperands: [], operator: expressionTree.operator, @@ -1290,7 +1290,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { entity: expressionTree.entity, returnFields: expressionTree.returnFields }; - expressionTree.filteringOperands.forEach(o => o instanceof FilteringExpressionsTree ? exprTreeCopy.filteringOperands.push(this.getExpressionTreeCopy(o)) : exprTreeCopy.filteringOperands.push(o)); + expressionTree.filteringOperands.forEach(o => o instanceof FilteringExpressionsTree ? exprTreeCopy.filteringOperands.push(this.getExpressionTreeCopy(o)) : exprTreeCopy.filteringOperands.push(o)); if (!this.innerQueryNewExpressionTree && shouldAssignInnerQueryExprTree) { this.innerQueryNewExpressionTree = exprTreeCopy; @@ -1444,7 +1444,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } - + if (expressionTree.entity) { entityName = expressionTree.entity; } @@ -1654,9 +1654,12 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.clearSelection(); this.cancelOperandAdd(); this.cancelOperandEdit(); - if(this.isAdvancedFiltering()){ - this.rootGroup = this.createExpressionGroupItem(this.expressionTree); - this.currentGroup = this.rootGroup; + this.rootGroup = this.createExpressionGroupItem(this.expressionTree); + this.currentGroup = this.rootGroup; + + if (this.rootGroup?.children?.length == 0) { + this.rootGroup = null; + this.currentGroup = null; } } } From 62c6fe9d56de71f2e12fc59156c9733889667b6e Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 31 Oct 2024 14:38:03 +0200 Subject: [PATCH 131/147] fix(query-builder): don't recreate rootgroup if the same --- .../query-builder-tree.component.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index abedccc2b1e..702af6cc9cc 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1627,8 +1627,22 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.clearSelection(); this.cancelOperandAdd(); this.cancelOperandEdit(); - this.rootGroup = this.createExpressionGroupItem(this.expressionTree); - this.currentGroup = this.rootGroup; + const newRootGroup = this.createExpressionGroupItem(this.expressionTree); + + // Ignore values of 'parent' and 'hovered' properties for the comparison + const parentPropReplacer = function replacer(key, value) { + if (key === "parent" || key === "hovered") { + return undefined; + } else { + return value; + } + }; + + // Skip root being recreated if the same + if (JSON.stringify(this.rootGroup, parentPropReplacer) !== JSON.stringify(newRootGroup, parentPropReplacer)) { // TODO: fix - not working for inner query STATE, add test + this.rootGroup = this.createExpressionGroupItem(this.expressionTree); + this.currentGroup = this.rootGroup; + } } } From 89f6d8bdb31feef38bbcbc4d7e5769e2afd36c83 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 31 Oct 2024 14:39:23 +0200 Subject: [PATCH 132/147] chore(*): remove leftover comment --- .../src/lib/query-builder/query-builder-tree.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 702af6cc9cc..47849e2e9ee 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1627,7 +1627,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.clearSelection(); this.cancelOperandAdd(); this.cancelOperandEdit(); - const newRootGroup = this.createExpressionGroupItem(this.expressionTree); // Ignore values of 'parent' and 'hovered' properties for the comparison const parentPropReplacer = function replacer(key, value) { @@ -1639,7 +1638,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { }; // Skip root being recreated if the same - if (JSON.stringify(this.rootGroup, parentPropReplacer) !== JSON.stringify(newRootGroup, parentPropReplacer)) { // TODO: fix - not working for inner query STATE, add test + const newRootGroup = this.createExpressionGroupItem(this.expressionTree); + if (JSON.stringify(this.rootGroup, parentPropReplacer) !== JSON.stringify(newRootGroup, parentPropReplacer)) { this.rootGroup = this.createExpressionGroupItem(this.expressionTree); this.currentGroup = this.rootGroup; } From f7118a2948e67a3d9f842a0ec64acbd87b8dd60b Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Thu, 31 Oct 2024 18:06:04 +0200 Subject: [PATCH 133/147] test(query-builder): fix several legacy tests --- .../query-builder-functions.spec.ts | 19 +-- .../query-builder.component.spec.ts | 128 +++++++++--------- 2 files changed, 73 insertions(+), 74 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index 2ef82e35ce1..eb3b443dc6f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -669,9 +669,9 @@ export class QueryBuilderFunctions { let i = 0; tabElements.forEach((element: HTMLElement) => { switch (i) { - case 0: expect(element).toHaveClass('igx-filter-tree__line--and'); break; + case 0: expect(element).toHaveClass('igx-input-group__input'); break; case 1: expect(element).toHaveClass('igx-input-group__input'); break; - case 2: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-filter-tree__line--and'); break; case 3: expect(element).toHaveClass('igx-chip'); break; case 4: expect(element).toHaveClass('igx-chip__remove'); break; case 5: expect(element).toHaveClass('igx-filter-tree__details-button'); break; @@ -728,9 +728,9 @@ export class QueryBuilderFunctions { let i = 0; tabElements.forEach((element: HTMLElement) => { switch (i) { - case 0: expect(element).toHaveClass('igx-filter-tree__line--and'); break; + case 0: expect(element).toHaveClass('igx-input-group__input'); break; case 1: expect(element).toHaveClass('igx-input-group__input'); break; - case 2: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-filter-tree__line--and'); break; case 3: expect(element).toHaveClass('igx-chip'); break; case 4: expect(element).toHaveClass('igx-chip__remove'); break; case 5: expect(element).toHaveClass('igx-chip'); break; @@ -853,17 +853,18 @@ export class QueryBuilderFunctions { } public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); + // Enter values in the nested query + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false, level); - // Enter values in the nested query - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); tick(100); fix.detectChanges(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, false, false, false, false, level); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false, level); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1, level); // Select 'ProductName' column. diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index ccc8d4a05c6..fac69e0dea4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -145,10 +145,10 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'or'); // Verify the enabled/disabled state of each input of the expression in edit mode. - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, false, false, false, false); // Verify the edit inputs are empty. - QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', '', '', '', ''); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); // Verify adding buttons are disabled. const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); @@ -159,12 +159,18 @@ describe('IgxQueryBuilder', () => { it(`Should discard newly added group when clicking on the 'cancel' button of its initial condition.`, fakeAsync(() => { spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); fix.detectChanges(); - expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(0); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); // Verify there is a new root group, which is empty. let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); @@ -267,7 +273,7 @@ describe('IgxQueryBuilder', () => { // adding buttons should be enabled, 'end group' button should be disabled addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); - expect(addingButtons.length).toBe(4); + expect(addingButtons.length).toBe(3); for (let i = 0; i < addingButtons.length; i++) { if (i === 3) { ControlsFunction.verifyButtonIsDisabled(addingButtons[i] as HTMLElement); @@ -385,6 +391,10 @@ describe('IgxQueryBuilder', () => { })); it('Should discard the added group when clicking its operator line without having a single expression.', fakeAsync(() => { + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); @@ -404,21 +414,22 @@ describe('IgxQueryBuilder', () => { })); it('Should be able to add and define a new group through initial adding button.', fakeAsync(() => { + expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); + // Select an entity + // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); fix.detectChanges(); // Verify the enabled/disabled state of each input of the expression in edit mode. - expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + expect(fix.componentInstance.queryBuilder.expressionTree).toBeDefined(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, false, false, false, false); - // Select an entity - // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false); // Select fields QueryBuilderFunctions.selectFieldsInEditModeExpression(fix, [2, 3]) tick(100); @@ -445,6 +456,8 @@ describe('IgxQueryBuilder', () => { //Commit the group QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + tick(100); + fix.detectChanges(); //Verify that expressionTree is correct const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); @@ -457,7 +470,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_contains" }, - "conditionName": null, + "conditionName": "contains", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -597,13 +610,13 @@ describe('IgxQueryBuilder', () => { it('Operator dropdown should contain operators based on the column\'s datatype (\'boolean\').', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + // Select an entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); tick(100); fix.detectChanges(); - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -620,13 +633,13 @@ describe('IgxQueryBuilder', () => { it('Should correctly apply a \'string\' column condition through UI.', fakeAsync(() => { // Verify there is no expression. expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -671,15 +684,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Greater Than\' with \'number\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -724,15 +734,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Equals\' with \'number\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -777,15 +784,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'boolean\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -825,15 +829,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with unary operator.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -874,15 +875,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with value from calendar.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -906,9 +904,6 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'in\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); @@ -918,6 +913,10 @@ describe('IgxQueryBuilder', () => { tick(100); fix.detectChanges(); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. @@ -950,7 +949,7 @@ describe('IgxQueryBuilder', () => { "isNestedQuery": true, "iconName": "in" }, - "conditionName": null, + "conditionName": "in", "ignoreCase": true, "searchVal": null, "searchTree": { @@ -962,7 +961,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_contains" }, - "conditionName": null, + "conditionName": "contains", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -991,15 +990,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'not-in\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -2273,11 +2269,13 @@ describe('IgxQueryBuilder', () => { // Verify the nested query is collapsed expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); - // Start editing expression in the nested query - QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); + // Double-click the existing chip to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true); tick(50); fix.detectChanges(); - QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true, 1); + + // Start editing expression in the nested query + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); tick(50); fix.detectChanges(); expect(queryBuilder.canCommit()).toBeTrue(); From 3a37c799d2a516ead3350297e3854767a4c76c7a Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Thu, 31 Oct 2024 18:08:48 +0200 Subject: [PATCH 134/147] feat(query-builder): add test for editing condition of inner query --- .../query-builder.component.spec.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index a4f261b80f3..f0238a81e17 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -2087,6 +2087,35 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); expect(queryBuilder.canCommit()).toBeTrue(); })); + + it(`Should be able to enter edit mode from condition in an inner query.`, fakeAsync(() => { + queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Expand the nested query + const toggleBtn = fix.debugElement.query(By.css('.igx-filter-tree__details-button')).nativeElement; + toggleBtn.click(); + tick(100); + fix.detectChanges(); + + // Double-click the child chip 'Released' to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true, 1); + tick(50); + fix.detectChanges(); + + // Verify both parent and child commit buttons are enabled + let parentCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix); + let childCommitBtn = QueryBuilderFunctions.getQueryBuilderExpressionCommitButton(fix, 1); + + ControlsFunction.verifyButtonIsDisabled(parentCommitBtn as HTMLElement); + ControlsFunction.verifyButtonIsDisabled(childCommitBtn as HTMLElement, false); + + // Verify inputs values on both levels + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'OrderId', 'In', '', 0); + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, 'Released', 'True', '', 1); + })); }); describe('API', () => { From 1b1b487a4f182f5d2eed4ccc3d4c700e9302bca6 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Thu, 31 Oct 2024 22:23:29 +0200 Subject: [PATCH 135/147] fix(query-builder): cancommit logic and test --- .../lib/query-builder/query-builder-tree.component.ts | 4 ++-- .../lib/query-builder/query-builder.component.spec.ts | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 8f443415717..dba65913ba4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -852,8 +852,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } else { return this.selectedReturnFields?.length > 0 && ( - (!this._editedExpression && !!this.rootGroup) || // no edited expr, root group - (this._editedExpression && !this.selectedField && (this.expressionTree && this.expressionTree.filteringOperands[0] !== this._editedExpression.expression)) || // empty edited expr with at least one other expr + (!this._editedExpression) || // no edited expr + (this._editedExpression && !this.selectedField) || // empty edited expr (this._editedExpression && this.operandCanBeCommitted() === true) // valid edited expr ); } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index fac69e0dea4..075994e5549 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -2044,12 +2044,10 @@ describe('IgxQueryBuilder', () => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; const bodyElement = queryBuilderElement.children[1].children[0]; const actionArea = bodyElement.children[0].querySelector('.igx-query-builder__root-actions'); - expect(actionArea).toBeDefined('action area is missing'); - expect(actionArea).not.toBeNull('action area is missing'); - expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); - expect(bodyElement.children[0].children[1].children[6]).toHaveClass('igx-query-builder-tree'); - expect(bodyElement.children[0].children[1].children[6].children.length).toEqual(3); - const tree = bodyElement.children[0].children[1].children[6].querySelector('.igx-filter-tree__expression'); + expect(actionArea).toBeNull(); + expect(bodyElement.children[1].children[1].children[5]).toHaveClass('igx-query-builder-tree'); + expect(bodyElement.children[1].children[1].children[5].children.length).toEqual(3); + const tree = bodyElement.children[1].children[1].children[5].querySelector('.igx-filter-tree__expression'); expect(tree).toBeNull(); })); From f917b4a412ad9fe2f5cb6e8a28fcae7a04565849 Mon Sep 17 00:00:00 2001 From: ivanvpetrov <110455887+ivanvpetrov@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:12:13 +0200 Subject: [PATCH 136/147] Query builder simple select from (#14985) * refactor(query-builder): simple select from * test(query-builder): fix several legacy tests --------- Co-authored-by: INFRAGISTICS\IPetrov Co-authored-by: igdmdimitrov --- .../query-builder-functions.spec.ts | 42 ++- .../query-builder-tree.component.html | 111 +++--- .../query-builder-tree.component.ts | 84 +++-- .../query-builder.component.spec.ts | 333 ++++++++++++------ 4 files changed, 361 insertions(+), 209 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index 210ab0bf62d..eb3b443dc6f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -530,10 +530,10 @@ export class QueryBuilderFunctions { public static verifyEditModeQueryExpressionInputStates(fix, entitySelectEnabled: boolean, fieldComboEnabled: boolean, - columnSelectEnabled: boolean, - operatorSelectEnabled: boolean, - valueInputEnabled: boolean, - commitButtonEnabled: boolean, + columnSelectEnabled: boolean = null, + operatorSelectEnabled: boolean = null, + valueInputEnabled: boolean = null, + commitButtonEnabled: boolean = null, level = 0) { // Verify the entity select state. const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('igx-input-group'); @@ -544,7 +544,9 @@ export class QueryBuilderFunctions { expect(!fieldInputGroup.classList.contains('igx-input-group--disabled')).toBe(fieldComboEnabled, 'incorrect fields combo state'); - QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, columnSelectEnabled, operatorSelectEnabled, valueInputEnabled, commitButtonEnabled, level); + if(columnSelectEnabled || operatorSelectEnabled || valueInputEnabled || commitButtonEnabled){ + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, columnSelectEnabled, operatorSelectEnabled, valueInputEnabled, commitButtonEnabled, level); + } }; public static verifyEditModeExpressionInputStates(fix, @@ -581,15 +583,18 @@ export class QueryBuilderFunctions { public static verifyQueryEditModeExpressionInputValues(fix, entityText: string, fieldsText: string, - columnText: string, - operatorText: string, - valueText: string, + columnText: string = null, + operatorText: string = null, + valueText: string = null, level = 0) { const entityInput = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('input'); const fieldInput = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fix, level).querySelector('input'); - QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, columnText, operatorText, valueText, level); expect(entityInput.value).toBe(entityText); expect(fieldInput.value).toBe(fieldsText); + + if(columnText || operatorText || valueText){ + QueryBuilderFunctions.verifyEditModeExpressionInputValues(fix, columnText, operatorText, valueText, level); + } }; public static verifyEditModeExpressionInputValues(fix, @@ -664,9 +669,9 @@ export class QueryBuilderFunctions { let i = 0; tabElements.forEach((element: HTMLElement) => { switch (i) { - case 0: expect(element).toHaveClass('igx-filter-tree__line--and'); break; + case 0: expect(element).toHaveClass('igx-input-group__input'); break; case 1: expect(element).toHaveClass('igx-input-group__input'); break; - case 2: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-filter-tree__line--and'); break; case 3: expect(element).toHaveClass('igx-chip'); break; case 4: expect(element).toHaveClass('igx-chip__remove'); break; case 5: expect(element).toHaveClass('igx-filter-tree__details-button'); break; @@ -723,9 +728,9 @@ export class QueryBuilderFunctions { let i = 0; tabElements.forEach((element: HTMLElement) => { switch (i) { - case 0: expect(element).toHaveClass('igx-filter-tree__line--and'); break; + case 0: expect(element).toHaveClass('igx-input-group__input'); break; case 1: expect(element).toHaveClass('igx-input-group__input'); break; - case 2: expect(element).toHaveClass('igx-input-group__input'); break; + case 2: expect(element).toHaveClass('igx-filter-tree__line--and'); break; case 3: expect(element).toHaveClass('igx-chip'); break; case 4: expect(element).toHaveClass('igx-chip__remove'); break; case 5: expect(element).toHaveClass('igx-chip'); break; @@ -848,17 +853,18 @@ export class QueryBuilderFunctions { } public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); + // Enter values in the nested query + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false, level); - // Enter values in the nested query - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); tick(100); fix.detectChanges(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, false, false, false, false, level); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false, level); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1, level); // Select 'ProductName' column. diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index b05c8d94786..bd561a83a11 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -6,12 +6,66 @@ + +
+ + + + {{entity.name}} + + + + + +
+ + +
+ {{ this.resourceStrings.igx_query_builder_select_all }} +
+
+
+ +
+
+
+
- + +
-
(click)="onGroupClick(expressionItem)" >
-
-
- - - - - {{entity.name}} - - - - - -
- - -
- {{ this.resourceStrings.igx_query_builder_select_all }} -
-
-
- -
-
-
- +
f.field); if (this._expressionTree) { + this._expressionTree.entity = this._entityNewValue.name; + this._expressionTree.returnFields = []; + this._expressionTree.filteringOperands = []; + this._editedExpression = null; if (!this.parentExpression) { this.expressionTreeChange.emit(this._expressionTree); } - this.addAndGroup(); + this.rootGroup = null; + this.currentGroup = this.rootGroup; } this._selectedField = null; @@ -611,6 +625,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._entityNewValue = null; this.innerQueryNewExpressionTree = null; + + this.initExpressionTree(this._selectedEntity.name, this.selectedReturnFields); } /** @@ -812,7 +828,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ public operandCanBeCommitted(): boolean { const innerQuery = this.innerQueries.filter(q => q.isInEditMode())[0]; - + return this.selectedField && this.selectedCondition && ( ( @@ -820,7 +836,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { !(this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery) ) || ( - this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery && innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined && innerQuery.selectedReturnFields.length > 0 + this.selectedField?.filters?.condition(this.selectedCondition)?.isNestedQuery && innerQuery && !!innerQuery.expressionTree && innerQuery._editedExpression == undefined && innerQuery.selectedReturnFields?.length > 0 ) || this.selectedField.filters.condition(this.selectedCondition)?.isUnary ); @@ -836,8 +852,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } else { return this.selectedReturnFields?.length > 0 && ( - (!this._editedExpression && !!this.rootGroup) || // no edited expr, root group - (this._editedExpression && !this.selectedField && (this.expressionTree && this.expressionTree.filteringOperands[0] !== this._editedExpression.expression)) || // empty edited expr with at least one other expr + (!this._editedExpression) || // no edited expr + (this._editedExpression && !this.selectedField) || // empty edited expr (this._editedExpression && this.operandCanBeCommitted() === true) // valid edited expr ); } @@ -1272,7 +1288,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { return null; } - const exprTreeCopy = + const exprTreeCopy = { filteringOperands: [], operator: expressionTree.operator, @@ -1280,7 +1296,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { entity: expressionTree.entity, returnFields: expressionTree.returnFields }; - expressionTree.filteringOperands.forEach(o => o instanceof FilteringExpressionsTree ? exprTreeCopy.filteringOperands.push(this.getExpressionTreeCopy(o)) : exprTreeCopy.filteringOperands.push(o)); + expressionTree.filteringOperands.forEach(o => o instanceof FilteringExpressionsTree ? exprTreeCopy.filteringOperands.push(this.getExpressionTreeCopy(o)) : exprTreeCopy.filteringOperands.push(o)); if (!this.innerQueryNewExpressionTree && shouldAssignInnerQueryExprTree) { this.innerQueryNewExpressionTree = exprTreeCopy; @@ -1300,6 +1316,20 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } + public onReturnFieldSelectChanging(event: IComboSelectionChangingEventArgs) { + this.initExpressionTree(this.selectedEntity.name, event.newSelection.map(item => item.field)) + } + + public initExpressionTree(selectedEntityName: string, selectedReturnFields: string[]) { + if (!this._expressionTree) { + this._expressionTree = this.createExpressionTreeFromGroupItem(new ExpressionGroupItem(FilteringLogic.And, this.rootGroup), selectedEntityName, selectedReturnFields); + } + + if (!this.parentExpression) { + this.expressionTreeChange.emit(this._expressionTree); + } + } + public getSearchValueTemplateContext(defaultSearchValueTemplate): any { const ctx = { $implicit: this.searchValue, @@ -1411,26 +1441,29 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { ignoreCase: filteringExpr.ignoreCase }; const operandItem = new ExpressionOperandItem(exprCopy, groupItem); - if (expressionTree.entity) { - entityName = expressionTree.entity; - } - const entity = this.entities?.find(el => el.name === entityName); - if (entity) { - this.fields = entity.fields; - } const field = this.fields?.find(el => el.field === filteringExpr.fieldName); operandItem.fieldLabel = field?.label || field?.header || field?.field; if (this._expandedExpressions.filter(e => e.searchTree == operandItem.expression.searchTree).length > 0) { operandItem.expanded = true; } groupItem.children.push(operandItem); - this._selectedEntity = this.entities?.find(el => el.name === entityName); - this._selectedReturnFields = - !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') - ? this.fields?.map(f => f.field) - : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } } + + + if (expressionTree.entity) { + entityName = expressionTree.entity; + } + const entity = this.entities?.find(el => el.name === entityName); + if (entity) { + this.fields = entity.fields; + } + + this._selectedEntity = this.entities?.find(el => el.name === entityName); + this._selectedReturnFields = + !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') + ? this.fields?.map(f => f.field) + : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } return groupItem; } @@ -1505,7 +1538,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { if (!expressionItem.parent) { this.rootGroup = null; this.currentGroup = null; - this._expressionTree = null; + //this._expressionTree = null; return; } @@ -1643,6 +1676,11 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.rootGroup = this.createExpressionGroupItem(this.expressionTree); this.currentGroup = this.rootGroup; } + + if (this.rootGroup?.children?.length == 0) { + this.rootGroup = null; + this.currentGroup = null; + } } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index f0238a81e17..f2ef4114b8c 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -50,35 +50,34 @@ describe('IgxQueryBuilder', () => { const bodyElement = queryTreeElement.children[0]; expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); expect(bodyElement.children.length).toEqual(2); - expect(bodyElement.children[0]).toHaveClass('igx-query-builder__root'); - const actionArea = bodyElement.children[0].querySelector('.igx-query-builder__root-actions'); - // initial add "'and'/'or' group " buttons should be displayed - expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); - // empty filtering tree message should be displayed - expect(bodyElement.children[0].children[1]).toHaveClass('igx-filter-empty'); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', ''); }); - it('Should render Query Builder with initially set expression tree properly.', () => { + it('Should render Query Builder with initially set expression tree properly.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); + tick(100); + fix.detectChanges(); const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; const bodyElement = queryTreeElement.children[0]; expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); - expect(bodyElement.children.length).toEqual(2); + expect(bodyElement.children.length).toEqual(3); // initial add "'and'/'or' group " buttons and empty filtering tree message should NOT be displayed expect(bodyElement.querySelectorAll(':scope > button').length).toEqual(0); - expect(bodyElement.children[0]).toHaveClass('igx-filter-tree'); + expect(bodyElement.children[1]).toHaveClass('igx-filter-tree'); // Verify the operator line of the root group is an 'And' line. QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); // all inputs should be displayed correctly - const queryTreeExpressionContainer = bodyElement.children[0].children[1]; + const selectFromContainer = bodyElement.children[0]; + expect(selectFromContainer).toHaveClass('igx-filter-tree__inputs'); + expect(selectFromContainer.children[0].tagName).toEqual('IGX-SELECT'); + expect(selectFromContainer.children[1].tagName).toEqual('IGX-COMBO'); + const queryTreeExpressionContainer = bodyElement.children[1].children[1]; expect(queryTreeExpressionContainer).toHaveClass('igx-filter-tree__expression'); - expect(queryTreeExpressionContainer.children[0]).toHaveClass('igx-filter-tree__inputs'); - expect(queryTreeExpressionContainer.children[0].children[0].tagName).toEqual('IGX-SELECT'); - expect(queryTreeExpressionContainer.children[0].children[1].tagName).toEqual('IGX-COMBO'); const expressionItems = queryTreeExpressionContainer.querySelectorAll(':scope > .igx-filter-tree__expression-item'); expect(expressionItems.length).toEqual(queryBuilder.expressionTree.filteringOperands.length); @@ -96,11 +95,15 @@ describe('IgxQueryBuilder', () => { for (const button of buttons) { ControlsFunction.verifyButtonIsDisabled(button as HTMLElement, false); } - }); + })); }); describe('Interactions', () => { it('Should correctly initialize a newly added \'And\' group.', fakeAsync(() => { + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + // Click the initial 'Add And Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); @@ -113,10 +116,10 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'and'); // Verify the enabled/disabled state of each input of the expression in edit mode. - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false); // Verify the edit inputs are empty. - QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', '', '', '', ''); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); // Verify adding buttons are disabled. const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); @@ -126,6 +129,10 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => { + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); @@ -138,10 +145,10 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyOperatorLine(QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement, 'or'); // Verify the enabled/disabled state of each input of the expression in edit mode. - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, false, false, false, false); // Verify the edit inputs are empty. - QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', '', '', '', ''); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); // Verify adding buttons are disabled. const buttons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); @@ -152,12 +159,18 @@ describe('IgxQueryBuilder', () => { it(`Should discard newly added group when clicking on the 'cancel' button of its initial condition.`, fakeAsync(() => { spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); fix.detectChanges(); - expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(0); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); // Verify there is a new root group, which is empty. let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); @@ -260,7 +273,7 @@ describe('IgxQueryBuilder', () => { // adding buttons should be enabled, 'end group' button should be disabled addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); - expect(addingButtons.length).toBe(4); + expect(addingButtons.length).toBe(3); for (let i = 0; i < addingButtons.length; i++) { if (i === 3) { ControlsFunction.verifyButtonIsDisabled(addingButtons[i] as HTMLElement); @@ -378,6 +391,10 @@ describe('IgxQueryBuilder', () => { })); it('Should discard the added group when clicking its operator line without having a single expression.', fakeAsync(() => { + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); @@ -397,21 +414,22 @@ describe('IgxQueryBuilder', () => { })); it('Should be able to add and define a new group through initial adding button.', fakeAsync(() => { + expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); + // Select an entity + // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); tick(100); fix.detectChanges(); // Verify the enabled/disabled state of each input of the expression in edit mode. - expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false, false, false, false, false); + expect(fix.componentInstance.queryBuilder.expressionTree).toBeDefined(); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, false, false, false, false); - // Select an entity - // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true, true, false, false, false); // Select fields QueryBuilderFunctions.selectFieldsInEditModeExpression(fix, [2, 3]) tick(100); @@ -438,6 +456,8 @@ describe('IgxQueryBuilder', () => { //Commit the group QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + tick(100); + fix.detectChanges(); //Verify that expressionTree is correct const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); @@ -450,7 +470,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_contains" }, - "conditionName": null, + "conditionName": "contains", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -590,13 +610,13 @@ describe('IgxQueryBuilder', () => { it('Operator dropdown should contain operators based on the column\'s datatype (\'boolean\').', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + // Select an entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); tick(100); fix.detectChanges(); - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -613,13 +633,13 @@ describe('IgxQueryBuilder', () => { it('Should correctly apply a \'string\' column condition through UI.', fakeAsync(() => { // Verify there is no expression. expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -664,15 +684,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Greater Than\' with \'number\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -717,15 +734,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Equals\' with \'number\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -770,15 +784,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'boolean\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -818,15 +829,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with unary operator.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -867,15 +875,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with value from calendar.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -899,9 +904,6 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'in\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); @@ -911,6 +913,10 @@ describe('IgxQueryBuilder', () => { tick(100); fix.detectChanges(); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. @@ -943,7 +949,7 @@ describe('IgxQueryBuilder', () => { "isNestedQuery": true, "iconName": "in" }, - "conditionName": null, + "conditionName": "in", "ignoreCase": true, "searchVal": null, "searchTree": { @@ -955,7 +961,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_contains" }, - "conditionName": null, + "conditionName": "contains", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -984,15 +990,12 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'not-in\' column condition through UI.', fakeAsync(() => { - // Verify there is no expression. - expect(queryBuilder.expressionTree).toBeUndefined(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -1169,6 +1172,7 @@ describe('IgxQueryBuilder', () => { fix.detectChanges(); tick(100); fix.detectChanges(); + // Verify actions container is not visible. (This container contains the 'edit' and the 'add' buttons.) expect(QueryBuilderFunctions.getQueryBuilderTreeExpressionActionsContainer(fix, [0])) .toBeNull('actions container is visible'); @@ -1270,6 +1274,10 @@ describe('IgxQueryBuilder', () => { it('Should select/deselect all child conditions and groups when clicking a group\'s operator line.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + const childGroup = new FilteringExpressionsTree(FilteringLogic.Or, undefined, undefined); childGroup.filteringOperands.push({ fieldName: 'OrderNameName', @@ -1479,6 +1487,10 @@ describe('IgxQueryBuilder', () => { tick(100); fix.detectChanges(); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + // Verify all inputs QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Orders', 'OrderId, OrderName, OrderDate, Delivered', '', '', ''); })); @@ -1529,6 +1541,8 @@ describe('IgxQueryBuilder', () => { it(`Should display 'expand'/'collapse' button properly.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); + tick(100); + fix.detectChanges(); const queryTreeElement: HTMLElement = fix.debugElement.queryAll(By.css(QueryBuilderConstants.QUERY_BUILDER_TREE))[0].nativeElement; // Nested query tree should have expand collapse button @@ -1718,6 +1732,8 @@ describe('IgxQueryBuilder', () => { it(`Should toggle the nested query on 'expand'/'collapse' button click.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); + tick(100); + fix.detectChanges(); expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); @@ -1741,7 +1757,9 @@ describe('IgxQueryBuilder', () => { it('Should create an "and"/"or" group on context menu button click and delete conditions on "delete filters" click.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + // Verify group types initially QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); @@ -1788,7 +1806,9 @@ describe('IgxQueryBuilder', () => { it('Ungroup button of the root group\'s context menu should be disabled.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + // Click root group's operator line. const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; rootOperatorLine.click(); @@ -1806,7 +1826,9 @@ describe('IgxQueryBuilder', () => { it('Should remove a group from the expr tree when clicking "delete" from the context menu.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); //create group @@ -1833,7 +1855,9 @@ describe('IgxQueryBuilder', () => { it('Should be able to open edit mode when condition is selected, close the edited condition on "close" button click and not commit it.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + //Select chip QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1]); tick(200); @@ -1872,7 +1896,9 @@ describe('IgxQueryBuilder', () => { it('Selecting/deselecting multiple conditions should display/hide the (create group)/(delete filters) context menu properly.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); //select 1 @@ -1911,7 +1937,9 @@ describe('IgxQueryBuilder', () => { it(`Should show/hide the group's context menu when clicking its operator line and should close the context menu when clicking its close button.`, fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const rootOperatorLine = QueryBuilderFunctions.getQueryBuilderTreeRootGroupOperatorLine(fix) as HTMLElement; QueryBuilderFunctions.verifyGroupContextMenuVisibility(fix, false); @@ -1945,7 +1973,9 @@ describe('IgxQueryBuilder', () => { it('Should be able to group, change And/Or and un-group conditions.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 0); @@ -1992,7 +2022,9 @@ describe('IgxQueryBuilder', () => { it('Should not make bug where existing inner query is leaking to a newly created one', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix) as HTMLElement; // Add new 'expression'. @@ -2012,12 +2044,10 @@ describe('IgxQueryBuilder', () => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; const bodyElement = queryBuilderElement.children[1].children[0]; const actionArea = bodyElement.children[0].querySelector('.igx-query-builder__root-actions'); - expect(actionArea).toBeDefined('action area is missing'); - expect(actionArea).not.toBeNull('action area is missing'); - expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); - expect(bodyElement.children[0].children[1].children[6]).toHaveClass('igx-query-builder-tree'); - expect(bodyElement.children[0].children[1].children[6].children.length).toEqual(3); - const tree = bodyElement.children[0].children[1].children[6].querySelector('.igx-filter-tree__expression'); + expect(actionArea).toBeNull(); + expect(bodyElement.children[1].children[1].children[5]).toHaveClass('igx-query-builder-tree'); + expect(bodyElement.children[1].children[1].children[5].children.length).toEqual(3); + const tree = bodyElement.children[1].children[1].children[5].querySelector('.igx-filter-tree__expression'); expect(tree).toBeNull(); })); @@ -2046,46 +2076,105 @@ describe('IgxQueryBuilder', () => { })); it('canCommit should return the correct validity state of currently added condition.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + // Verify the Query Builder validity state while adding a condition. + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Entity selected').toBeTrue(); - // Verify the Query Builder validity state while adding a condition. - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity + // Click the 'Add condition' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Add condition clicked').toBeTrue(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Column selected').toBeFalse(); QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); - expect(queryBuilder.canCommit()).toBeFalse(); + expect(queryBuilder.canCommit()).withContext('Operator contains selected').toBeFalse(); const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix).querySelector('input'); UIInteractions.clickAndSendInputElementValue(input, 'a'); tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeTrue(); + expect(queryBuilder.canCommit()).withContext('Search value filled').toBeTrue(); // Click on the 'cancel' button const closeButton = QueryBuilderFunctions.getQueryBuilderExpressionCloseButton(fix); UIInteractions.simulateClickEvent(closeButton); tick(100); fix.detectChanges(); + expect(queryBuilder.canCommit()).withContext('Entity remains selected').toBeTrue(); // Verify the Query Builder validity state for UNARY condition. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); + expect(queryBuilder.canCommit()).withContext('Add condition clicked again').toBeTrue(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); + expect(queryBuilder.canCommit()).withContext('Column selected again').toBeFalse(); + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); + expect(queryBuilder.canCommit()).withContext('Unary operator selected').toBeTrue(); + })); + + + it('Should be able to commit nested query without where condition.', fakeAsync(() => { QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. + tick(100); fix.detectChanges(); - expect(queryBuilder.canCommit()).toBeFalse(); - QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); - expect(queryBuilder.canCommit()).toBeFalse(); - QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); - expect(queryBuilder.canCommit()).toBeTrue(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. + + // Enter values in the nested query + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, 1); // Select 'Products' entity + tick(100); + fix.detectChanges(); + + QueryBuilderFunctions.verifyEditModeExpressionInputStates(fix, true, true, false, true); // Parent commit button should be enabled + QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix); + fix.detectChanges(); + + //Verify that expressionTree is correct + const exprTree = JSON.stringify(fix.componentInstance.queryBuilder.expressionTree, null, 2); + expect(exprTree).toBe(`{ + "filteringOperands": [ + { + "fieldName": "OrderId", + "condition": { + "name": "in", + "isUnary": false, + "isNestedQuery": true, + "iconName": "in" + }, + "conditionName": "in", + "ignoreCase": true, + "searchVal": null, + "searchTree": { + "filteringOperands": [], + "operator": 0, + "entity": "Products", + "returnFields": [ + "Id", + "ProductName", + "OrderId", + "Released" + ] + } + } + ], + "operator": 0, + "entity": "Orders", + "returnFields": [ + "OrderId", + "OrderName", + "OrderDate", + "Delivered" + ] +}`); })); it(`Should be able to enter edit mode from condition in an inner query.`, fakeAsync(() => { @@ -2207,11 +2296,13 @@ describe('IgxQueryBuilder', () => { // Verify the nested query is collapsed expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeFalse(); - // Start editing expression in the nested query - QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); + // Double-click the existing chip to enter edit mode. + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true); tick(50); fix.detectChanges(); - QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [1], true, 1); + + // Start editing expression in the nested query + QueryBuilderFunctions.clickQueryBuilderTreeExpressionChip(fix, [0], true, 1); tick(50); fix.detectChanges(); expect(queryBuilder.canCommit()).toBeTrue(); @@ -2273,14 +2364,18 @@ describe('IgxQueryBuilder', () => { it('Should navigate with Tab/Shift+Tab through entity and fields inputs, chips, their respective delete icons and the operator lines.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.verifyQueryBuilderTabbableElements(fix); })); it('Should navigate with Tab/Shift+Tab through chips" "edit", "cancel" and "adding" buttons, fields of a condition in edit mode.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const chip = fix.debugElement.queryAll(By.directive(IgxChipComponent))[0]; QueryBuilderFunctions.hitKeyUponElementAndDetectChanges(fix, ' ', chip.nativeElement, 200); @@ -2302,7 +2397,9 @@ describe('IgxQueryBuilder', () => { //!Both Enter and Space should work queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const chip = fix.debugElement.queryAll(By.directive(IgxChipComponent))[0]; QueryBuilderFunctions.verifyChipSelectedState(chip.nativeElement, false); @@ -2330,7 +2427,9 @@ describe('IgxQueryBuilder', () => { //!Both Enter and Space should work queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + const line = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_OPERATOR_LINE_AND_CSS_CLASS}`))[0]; const chips = fix.debugElement.queryAll(By.directive(IgxChipComponent)); @@ -2367,7 +2466,9 @@ describe('IgxQueryBuilder', () => { //!Both Enter and Space should work queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); fix.detectChanges(); - + tick(100); + fix.detectChanges(); + // Verify the there are three chip expressions. QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 3, 6); @@ -2576,6 +2677,10 @@ describe('IgxQueryBuilder', () => { expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[0].textContent.trim()).toBe('My Cancel'); expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[1].textContent.trim()).toBe('My Confirm'); + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); + tick(100); + fix.detectChanges(); + QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Contains' operator. From 97d367f61b139450683d15e0e95ecf08db4475d4 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 1 Nov 2024 09:11:34 +0200 Subject: [PATCH 137/147] test(query-builder): fix more legacy tests --- .../query-builder-functions.spec.ts | 36 +-- .../query-builder.component.spec.ts | 255 +++--------------- 2 files changed, 51 insertions(+), 240 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index eb3b443dc6f..0dd95798de4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -830,29 +830,7 @@ export class QueryBuilderFunctions { fix.detectChanges(); } - public static addChildGroup(fix: ComponentFixture, groupType: number, level: number) { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1, level); // Select 'ProductName' column. - QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0, level); // Select 'Contains' operator. - const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix, false, level).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'a'); - tick(100); - fix.detectChanges(); - - //Commit the populated expression. - QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, level); - fix.detectChanges(); - } - - public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { + public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { // Enter values in the nested query QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity tick(100); @@ -908,4 +886,16 @@ export class QueryBuilderFunctions { tick(); fix.detectChanges(); } + + public static selectEntityAndClickInitialAddGroup(fix: ComponentFixture, entityIndex: number, groupIndex: number) { + // Select 'Orders' entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, entityIndex); + tick(100); + fix.detectChanges(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupIndex); + tick(100); + fix.detectChanges(); + } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index f2ef4114b8c..249aa964a71 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -129,14 +129,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 1); // Verify there is a new root group, which is empty. QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 0, 0); @@ -159,17 +152,10 @@ describe('IgxQueryBuilder', () => { it(`Should discard newly added group when clicking on the 'cancel' button of its initial condition.`, fakeAsync(() => { spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(0); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 1); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); // Verify there is a new root group, which is empty. @@ -391,14 +377,7 @@ describe('IgxQueryBuilder', () => { })); it('Should discard the added group when clicking its operator line without having a single expression.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 1); let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); expect(group).not.toBeNull('There is no root group.'); @@ -417,14 +396,7 @@ describe('IgxQueryBuilder', () => { expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); // Select an entity // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 1); // Verify the enabled/disabled state of each input of the expression in edit mode. expect(fix.componentInstance.queryBuilder.expressionTree).toBeDefined(); @@ -486,16 +458,7 @@ describe('IgxQueryBuilder', () => { })); it('Value input should be disabled for unary operator.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); - - // Select an entity - // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 1); //Select Column QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); @@ -507,15 +470,7 @@ describe('IgxQueryBuilder', () => { })); it('Fields dropdown should contain proper fields based on the entity.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Open fields dropdown and verify the items. QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fix); @@ -534,15 +489,8 @@ describe('IgxQueryBuilder', () => { it('Column dropdown should contain proper fields based on the entity.', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); // Open columns dropdown and verify the items. QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); @@ -556,15 +504,8 @@ describe('IgxQueryBuilder', () => { it('Operator dropdown should contain operators based on the column\'s datatype (\'string\' or \'number\' or \'date\').', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); // Select 'string' type column ('OrderName'). QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); @@ -610,15 +551,8 @@ describe('IgxQueryBuilder', () => { it('Operator dropdown should contain operators based on the column\'s datatype (\'boolean\').', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Select 'boolean' type column ('Released'). QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); @@ -634,14 +568,7 @@ describe('IgxQueryBuilder', () => { // Verify there is no expression. expect(queryBuilder.expressionTree).toBeUndefined(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. @@ -666,7 +593,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_starts_with" }, - "conditionName": null, + "conditionName": "startsWith", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -684,14 +611,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Greater Than\' with \'number\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'Id' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator @@ -716,7 +636,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_greater_than" }, - "conditionName": null, + "conditionName": "greaterThan", "ignoreCase": true, "searchVal": 5, "searchTree": null @@ -734,14 +654,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Equals\' with \'number\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'Id' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator @@ -766,7 +679,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_equal" }, - "conditionName": null, + "conditionName": "equals", "ignoreCase": true, "searchVal": 5, "searchTree": null @@ -784,15 +697,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'boolean\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); // Select 'Released' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); // Select 'True' operator. @@ -811,7 +716,7 @@ describe('IgxQueryBuilder', () => { "isUnary": true, "iconName": "filter_true" }, - "conditionName": null, + "conditionName": "true", "ignoreCase": true, "searchVal": null, "searchTree": null @@ -829,14 +734,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with unary operator.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 9); // Select 'This Year' operator. @@ -857,7 +755,7 @@ describe('IgxQueryBuilder', () => { "isUnary": true, "iconName": "filter_this_year" }, - "conditionName": null, + "conditionName": "thisYear", "ignoreCase": true, "searchVal": null, "searchTree": null @@ -875,14 +773,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with value from calendar.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator. @@ -904,14 +795,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'in\' column condition through UI.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); @@ -990,14 +874,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'not-in\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 11); // Select 'Not-In' operator. @@ -1031,7 +908,7 @@ describe('IgxQueryBuilder', () => { "isNestedQuery": true, "iconName": "not-in" }, - "conditionName": null, + "conditionName": "notIn", "ignoreCase": true, "searchVal": null, "searchTree": { @@ -1043,7 +920,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_contains" }, - "conditionName": null, + "conditionName": "contains", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -1074,13 +951,7 @@ describe('IgxQueryBuilder', () => { it('Should disable value fields when isNestedQuery condition is selected', fakeAsync(() => { //Run test for all data type fields of the Order entity for (let i = 0; i <= 3; i++) { - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, i); // Select 'OrderId','OrderName','OrderDate','Delivered' column. @@ -1323,15 +1194,7 @@ describe('IgxQueryBuilder', () => { const dialogOutlet = document.querySelector('.igx-dialog__window'); expect(dialog).toBeDefined(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Alert dialog should not be opened if there is no previous selection expect(dialog.checkVisibility()).toBeFalse(); @@ -1372,15 +1235,7 @@ describe('IgxQueryBuilder', () => { const dialogOutlet = document.querySelector('.igx-dialog__window'); expect(dialog).toBeDefined(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Alert dialog should not be opened if there is no previous selection expect(dialog.checkVisibility()).toBeFalse(); @@ -1425,15 +1280,7 @@ describe('IgxQueryBuilder', () => { const dialog = queryTreeElement.querySelector('igx-dialog'); expect(dialog).toBeDefined(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Alert dialog should not be opened if there is no previous selection expect(dialog.checkVisibility()).toBeFalse(); @@ -1448,15 +1295,7 @@ describe('IgxQueryBuilder', () => { })); it('Should reset all inputs when the entity is changed.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); //Select Column QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); @@ -1496,15 +1335,7 @@ describe('IgxQueryBuilder', () => { })); it('Should NOT reset all inputs when the entity is not changed.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); //Select Column QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); @@ -1681,19 +1512,12 @@ describe('IgxQueryBuilder', () => { })); it('Should collapse nested query when it is committed.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. - QueryBuilderFunctions.addChildGroup(fix, 0, 1); + QueryBuilderFunctions.addAndValidateChildGroup(fix, 0, 1); // Verify that the nested query is expanded expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeTrue(); @@ -2011,6 +1835,9 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 1); //Un-group OR group + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0]); + tick(200); + fix.detectChanges(); const unGroupButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'UnGroup'); unGroupButton.nativeElement.click(); tick(200); @@ -2118,13 +1945,7 @@ describe('IgxQueryBuilder', () => { it('Should be able to commit nested query without where condition.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. From 85a27b497f4f65b9b8bb157231315fb63c41ef89 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 1 Nov 2024 09:52:03 +0200 Subject: [PATCH 138/147] test(query-builder): fix more legacy tests --- .../query-builder-functions.spec.ts | 36 +-- .../query-builder.component.spec.ts | 255 +++--------------- 2 files changed, 51 insertions(+), 240 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index eb3b443dc6f..0dd95798de4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -830,29 +830,7 @@ export class QueryBuilderFunctions { fix.detectChanges(); } - public static addChildGroup(fix: ComponentFixture, groupType: number, level: number) { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupType, level); - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1, level); // Select 'ProductName' column. - QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0, level); // Select 'Contains' operator. - const input = QueryBuilderFunctions.getQueryBuilderValueInput(fix, false, level).querySelector('input'); - UIInteractions.clickAndSendInputElementValue(input, 'a'); - tick(100); - fix.detectChanges(); - - //Commit the populated expression. - QueryBuilderFunctions.clickQueryBuilderExpressionCommitButton(fix, level); - fix.detectChanges(); - } - - public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { + public static addAndValidateChildGroup(fix: ComponentFixture, groupType: number, level: number) { // Enter values in the nested query QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0, level); // Select 'Products' entity tick(100); @@ -908,4 +886,16 @@ export class QueryBuilderFunctions { tick(); fix.detectChanges(); } + + public static selectEntityAndClickInitialAddGroup(fix: ComponentFixture, entityIndex: number, groupIndex: number) { + // Select 'Orders' entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, entityIndex); + tick(100); + fix.detectChanges(); + + // Click the initial 'Add Or Group' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, groupIndex); + tick(100); + fix.detectChanges(); + } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index f2ef4114b8c..249aa964a71 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -129,14 +129,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly initialize a newly added \'Or\' group.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 1); // Verify there is a new root group, which is empty. QueryBuilderFunctions.verifyRootAndSubGroupExpressionsCount(fix, 0, 0); @@ -159,17 +152,10 @@ describe('IgxQueryBuilder', () => { it(`Should discard newly added group when clicking on the 'cancel' button of its initial condition.`, fakeAsync(() => { spyOn(queryBuilder.expressionTreeChange, 'emit').and.callThrough(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(0); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 1); + expect(queryBuilder.expressionTreeChange.emit).toHaveBeenCalledTimes(1); // Verify there is a new root group, which is empty. @@ -391,14 +377,7 @@ describe('IgxQueryBuilder', () => { })); it('Should discard the added group when clicking its operator line without having a single expression.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 1); let group = QueryBuilderFunctions.getQueryBuilderTreeRootGroup(fix); expect(group).not.toBeNull('There is no root group.'); @@ -417,14 +396,7 @@ describe('IgxQueryBuilder', () => { expect(fix.componentInstance.queryBuilder.expressionTree).toBeUndefined(); // Select an entity // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 1); // Verify the enabled/disabled state of each input of the expression in edit mode. expect(fix.componentInstance.queryBuilder.expressionTree).toBeDefined(); @@ -486,16 +458,7 @@ describe('IgxQueryBuilder', () => { })); it('Value input should be disabled for unary operator.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 1); - tick(100); - fix.detectChanges(); - - // Select an entity - // TO DO: refactor the methods when entity and fields drop-downs are in the correct overlay - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 1); //Select Column QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); @@ -507,15 +470,7 @@ describe('IgxQueryBuilder', () => { })); it('Fields dropdown should contain proper fields based on the entity.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Open fields dropdown and verify the items. QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fix); @@ -534,15 +489,8 @@ describe('IgxQueryBuilder', () => { it('Column dropdown should contain proper fields based on the entity.', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); // Open columns dropdown and verify the items. QueryBuilderFunctions.clickQueryBuilderColumnSelect(fix); @@ -556,15 +504,8 @@ describe('IgxQueryBuilder', () => { it('Operator dropdown should contain operators based on the column\'s datatype (\'string\' or \'number\' or \'date\').', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); // Select 'string' type column ('OrderName'). QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); @@ -610,15 +551,8 @@ describe('IgxQueryBuilder', () => { it('Operator dropdown should contain operators based on the column\'s datatype (\'boolean\').', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}`))[0].nativeElement; - // Select an entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Select 'boolean' type column ('Released'). QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); @@ -634,14 +568,7 @@ describe('IgxQueryBuilder', () => { // Verify there is no expression. expect(queryBuilder.expressionTree).toBeUndefined(); - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 1); // Select 'ProductName' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Starts With' operator. @@ -666,7 +593,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_starts_with" }, - "conditionName": null, + "conditionName": "startsWith", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -684,14 +611,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Greater Than\' with \'number\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'Id' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 2); // Select 'Greater Than' operator @@ -716,7 +636,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_greater_than" }, - "conditionName": null, + "conditionName": "greaterThan", "ignoreCase": true, "searchVal": 5, "searchTree": null @@ -734,14 +654,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'Equals\' with \'number\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'Id' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator @@ -766,7 +679,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_equal" }, - "conditionName": null, + "conditionName": "equals", "ignoreCase": true, "searchVal": 5, "searchTree": null @@ -784,15 +697,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'boolean\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 3); // Select 'Released' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 1); // Select 'True' operator. @@ -811,7 +716,7 @@ describe('IgxQueryBuilder', () => { "isUnary": true, "iconName": "filter_true" }, - "conditionName": null, + "conditionName": "true", "ignoreCase": true, "searchVal": null, "searchTree": null @@ -829,14 +734,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with unary operator.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 9); // Select 'This Year' operator. @@ -857,7 +755,7 @@ describe('IgxQueryBuilder', () => { "isUnary": true, "iconName": "filter_this_year" }, - "conditionName": null, + "conditionName": "thisYear", "ignoreCase": true, "searchVal": null, "searchTree": null @@ -875,14 +773,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply a \'date\' column condition through UI with value from calendar.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 2); // Select 'OrderDate' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 0); // Select 'Equals' operator. @@ -904,14 +795,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'in\' column condition through UI.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity tick(100); @@ -990,14 +874,7 @@ describe('IgxQueryBuilder', () => { })); it('Should correctly apply an \'not-in\' column condition through UI.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 11); // Select 'Not-In' operator. @@ -1031,7 +908,7 @@ describe('IgxQueryBuilder', () => { "isNestedQuery": true, "iconName": "not-in" }, - "conditionName": null, + "conditionName": "notIn", "ignoreCase": true, "searchVal": null, "searchTree": { @@ -1043,7 +920,7 @@ describe('IgxQueryBuilder', () => { "isUnary": false, "iconName": "filter_contains" }, - "conditionName": null, + "conditionName": "contains", "ignoreCase": true, "searchVal": "a", "searchTree": null @@ -1074,13 +951,7 @@ describe('IgxQueryBuilder', () => { it('Should disable value fields when isNestedQuery condition is selected', fakeAsync(() => { //Run test for all data type fields of the Order entity for (let i = 0; i <= 3; i++) { - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, i); // Select 'OrderId','OrderName','OrderDate','Delivered' column. @@ -1323,15 +1194,7 @@ describe('IgxQueryBuilder', () => { const dialogOutlet = document.querySelector('.igx-dialog__window'); expect(dialog).toBeDefined(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Alert dialog should not be opened if there is no previous selection expect(dialog.checkVisibility()).toBeFalse(); @@ -1372,15 +1235,7 @@ describe('IgxQueryBuilder', () => { const dialogOutlet = document.querySelector('.igx-dialog__window'); expect(dialog).toBeDefined(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Alert dialog should not be opened if there is no previous selection expect(dialog.checkVisibility()).toBeFalse(); @@ -1425,15 +1280,7 @@ describe('IgxQueryBuilder', () => { const dialog = queryTreeElement.querySelector('igx-dialog'); expect(dialog).toBeDefined(); - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); // Alert dialog should not be opened if there is no previous selection expect(dialog.checkVisibility()).toBeFalse(); @@ -1448,15 +1295,7 @@ describe('IgxQueryBuilder', () => { })); it('Should reset all inputs when the entity is changed.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); //Select Column QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); @@ -1496,15 +1335,7 @@ describe('IgxQueryBuilder', () => { })); it('Should NOT reset all inputs when the entity is not changed.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - // Select entity - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 0, 0); //Select Column QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); @@ -1681,19 +1512,12 @@ describe('IgxQueryBuilder', () => { })); it('Should collapse nested query when it is committed.', fakeAsync(() => { - // Click the initial 'Add Or Group' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. - QueryBuilderFunctions.addChildGroup(fix, 0, 1); + QueryBuilderFunctions.addAndValidateChildGroup(fix, 0, 1); // Verify that the nested query is expanded expect(fix.debugElement.query(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_TREE}--level-1`)).nativeElement.checkVisibility()).toBeTrue(); @@ -2011,6 +1835,9 @@ describe('IgxQueryBuilder', () => { QueryBuilderFunctions.verifyGroupLineCount(fix, 2, 1); //Un-group OR group + QueryBuilderFunctions.clickQueryBuilderTreeGroupOperatorLine(fix, [0]); + tick(200); + fix.detectChanges(); const unGroupButton = QueryBuilderFunctions.getQueryBuilderGroupContextMenuButton(contextMenu, 'UnGroup'); unGroupButton.nativeElement.click(); tick(200); @@ -2118,13 +1945,7 @@ describe('IgxQueryBuilder', () => { it('Should be able to commit nested query without where condition.', fakeAsync(() => { - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); // Select 'Orders' entity - tick(100); - fix.detectChanges(); - - QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); // Click the initial 'Add Or Group' button. - tick(100); - fix.detectChanges(); + QueryBuilderFunctions.selectEntityAndClickInitialAddGroup(fix, 1, 0); QueryBuilderFunctions.selectColumnInEditModeExpression(fix, 0); // Select 'OrderId' column. QueryBuilderFunctions.selectOperatorInEditModeExpression(fix, 10); // Select 'In' operator. From 8df21e13fc27cdc3fade11690db2d2e5a8b4f078 Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 1 Nov 2024 10:03:34 +0200 Subject: [PATCH 139/147] test(query-builder): fix empty Query Builder message --- .../query-builder.component.spec.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index 249aa964a71..d2abecd7384 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -33,7 +33,7 @@ describe('IgxQueryBuilder', () => { })); describe('Basic', () => { - it('Should render empty Query Builder properly.', () => { + it('Should render empty Query Builder properly.', fakeAsync(() => { const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; expect(queryBuilderElement).toBeDefined(); expect(queryBuilderElement.children.length).toEqual(2); @@ -51,9 +51,25 @@ describe('IgxQueryBuilder', () => { expect(bodyElement).toHaveClass(QueryBuilderConstants.QUERY_BUILDER_BODY); expect(bodyElement.children.length).toEqual(2); - QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true); + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, false); QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, '', ''); - }); + + // Select 'Orders' entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); + tick(100); + fix.detectChanges(); + + expect(bodyElement.children[1]).toHaveClass('igx-query-builder__root'); + + const actionArea = bodyElement.children[1].querySelector('.igx-query-builder__root-actions'); + // initial add "'and'/'or' group " buttons should be displayed + expect(actionArea.querySelectorAll(':scope > button').length).toEqual(2); + // empty filtering tree message should be displayed + expect(bodyElement.children[1].children[1]).toHaveClass('igx-filter-empty'); + + QueryBuilderFunctions.verifyEditModeQueryExpressionInputStates(fix, true, true); + QueryBuilderFunctions.verifyQueryEditModeExpressionInputValues(fix, 'Products', 'Id, ProductName, OrderId, Released'); + })); it('Should render Query Builder with initially set expression tree properly.', fakeAsync(() => { queryBuilder.expressionTree = QueryBuilderFunctions.generateExpressionTree(); From 9d68a9278220ffab92f0e2933ced652209e3e68a Mon Sep 17 00:00:00 2001 From: Galina Edinakova Date: Fri, 1 Nov 2024 10:08:33 +0200 Subject: [PATCH 140/147] fix(*): Guard the fields array where necessary. --- .../src/lib/query-builder/query-builder-tree.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index dba65913ba4..6b04c1ab752 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -950,7 +950,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.fields = this.selectedEntity ? this.selectedEntity.fields : null; this.selectedField = expressionItem.expression.fieldName ? - this.fields.find(field => field.field === expressionItem.expression.fieldName) + this.fields?.find(field => field.field === expressionItem.expression.fieldName) : null; this.selectedCondition = expressionItem.expression.condition ? @@ -1197,14 +1197,14 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public getFormatter(field: string) { - return this.fields.find(el => el.field === field)?.formatter; + return this.fields?.find(el => el.field === field)?.formatter; } /** * @hidden @internal */ public getFormat(field: string) { - return this.fields.find(el => el.field === field).pipeArgs.format; + return this.fields?.find(el => el.field === field).pipeArgs.format; } /** @@ -1264,7 +1264,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { const returnFields = innerTree.returnFields; let text = returnFields.join(', '); const innerTreeEntity = this.entities?.find(el => el.name === innerTree.entity); - if (returnFields.length === innerTreeEntity.fields.length) { + if (returnFields.length === innerTreeEntity?.fields.length) { text = this.resourceStrings.igx_query_builder_all_fields; } else { text = returnFields.join(', '); From 33199c2af2ad553b4ba6c1787c6ca12e35426d5f Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 1 Nov 2024 11:08:19 +0200 Subject: [PATCH 141/147] test(query-builder): fix localization test --- .../query-builder-functions.spec.ts | 15 +++++++------ .../query-builder.component.spec.ts | 21 ++++++++++++++++--- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts index 0dd95798de4..fce2f93b04f 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-functions.spec.ts @@ -530,10 +530,10 @@ export class QueryBuilderFunctions { public static verifyEditModeQueryExpressionInputStates(fix, entitySelectEnabled: boolean, fieldComboEnabled: boolean, - columnSelectEnabled: boolean = null, - operatorSelectEnabled: boolean = null, - valueInputEnabled: boolean = null, - commitButtonEnabled: boolean = null, + columnSelectEnabled?: boolean, + operatorSelectEnabled?: boolean, + valueInputEnabled?: boolean, + commitButtonEnabled?: boolean, level = 0) { // Verify the entity select state. const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('igx-input-group'); @@ -583,9 +583,9 @@ export class QueryBuilderFunctions { public static verifyQueryEditModeExpressionInputValues(fix, entityText: string, fieldsText: string, - columnText: string = null, - operatorText: string = null, - valueText: string = null, + columnText?: string, + operatorText?: string, + valueText?: string, level = 0) { const entityInput = QueryBuilderFunctions.getQueryBuilderEntitySelect(fix, level).querySelector('input'); const fieldInput = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fix, level).querySelector('input'); @@ -888,7 +888,6 @@ export class QueryBuilderFunctions { } public static selectEntityAndClickInitialAddGroup(fix: ComponentFixture, entityIndex: number, groupIndex: number) { - // Select 'Orders' entity QueryBuilderFunctions.selectEntityInEditModeExpression(fix, entityIndex); tick(100); fix.detectChanges(); diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index d2abecd7384..bccf848fcd2 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -34,6 +34,8 @@ describe('IgxQueryBuilder', () => { describe('Basic', () => { it('Should render empty Query Builder properly.', fakeAsync(() => { + tick(100); + fix.detectChanges(); const queryBuilderElement: HTMLElement = fix.debugElement.queryAll(By.css(`.${QueryBuilderConstants.QUERY_BUILDER_CLASS}`))[0].nativeElement; expect(queryBuilderElement).toBeDefined(); expect(queryBuilderElement.children.length).toEqual(2); @@ -2478,6 +2480,11 @@ describe('IgxQueryBuilder', () => { }); fix.detectChanges(); + // Select 'Orders' entity + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + tick(100); + fix.detectChanges(); + expect(QueryBuilderFunctions.getQueryBuilderHeaderText(fix)).toBe(' My advanced filter '); expect((QueryBuilderFunctions.getQueryBuilderHeaderLegendItemAnd(fix) as HTMLElement).innerText).toBe('My and'); expect((QueryBuilderFunctions.getQueryBuilderHeaderLegendItemOr(fix) as HTMLElement).innerText).toBe('My or'); @@ -2487,6 +2494,7 @@ describe('IgxQueryBuilder', () => { .toBe('My or group'); expect((QueryBuilderFunctions.getQueryBuilderEmptyPrompt(fix) as HTMLElement).innerText).toBe('My initial text'); + // Click the initial 'Add Or Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -2498,11 +2506,9 @@ describe('IgxQueryBuilder', () => { expect((QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[2] as HTMLElement).querySelector('span').innerText) .toBe('My or group'); - // Populate edit inputs. - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); // Select 'Products'. // Show changing entity alert dialog - QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 1); + QueryBuilderFunctions.selectEntityInEditModeExpression(fix, 0); tick(100); fix.detectChanges(); const dialogOutlet = document.querySelector('.igx-dialog__window'); @@ -2514,6 +2520,13 @@ describe('IgxQueryBuilder', () => { expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[0].textContent.trim()).toBe('My Cancel'); expect(dialogOutlet.querySelector('.igx-dialog__window-actions').children[1].textContent.trim()).toBe('My Confirm'); + const confirmButton = Array.from(dialogOutlet.querySelectorAll('button'))[1]; + confirmButton.click(); + fix.detectChanges(); + tick(100); + fix.detectChanges(); + + // Click the initial 'Add And Group' button. QueryBuilderFunctions.clickQueryBuilderInitialAddGroupButton(fix, 0); tick(100); fix.detectChanges(); @@ -2543,11 +2556,13 @@ describe('IgxQueryBuilder', () => { // Close context menu. QueryBuilderFunctions.clickQueryBuilderContextMenuCloseButton(fix); + tick(100); fix.detectChanges(); // Add another expression to root group. let btn = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0)[0] as HTMLElement; btn.click(); + tick(100); fix.detectChanges(); // Populate edit inputs. From 8852677f0ba48e226b7817bbe9894a0ec138f3fc Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 1 Nov 2024 14:01:14 +0200 Subject: [PATCH 142/147] feat(query-builder): fix current group change on new group creation --- .../src/lib/query-builder/query-builder-tree.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 6b04c1ab752..e3d35b4c1d2 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1662,8 +1662,8 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this.cancelOperandEdit(); // Ignore values of 'parent' and 'hovered' properties for the comparison - const parentPropReplacer = function replacer(key, value) { - if (key === "parent" || key === "hovered") { + const propsReplacer = function replacer(key, value) { + if (key === "parent" || key === "hovered" || key === "ignoreCase" || key === "inEditMode") { return undefined; } else { return value; @@ -1672,7 +1672,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { // Skip root being recreated if the same const newRootGroup = this.createExpressionGroupItem(this.expressionTree); - if (JSON.stringify(this.rootGroup, parentPropReplacer) !== JSON.stringify(newRootGroup, parentPropReplacer)) { + if (JSON.stringify(this.rootGroup, propsReplacer) !== JSON.stringify(newRootGroup, propsReplacer)) { this.rootGroup = this.createExpressionGroupItem(this.expressionTree); this.currentGroup = this.rootGroup; } From 8c643fb1e6edfb2c5ee40289c281926dc7aeef98 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 1 Nov 2024 14:31:11 +0200 Subject: [PATCH 143/147] feat(query-builder): don't show inner tree if not in edit mode and no conditions --- .../src/lib/query-builder/query-builder-tree.component.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html index bd561a83a11..e777717851b 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.html @@ -459,7 +459,9 @@
- + Date: Fri, 1 Nov 2024 14:57:21 +0200 Subject: [PATCH 144/147] feat(query-builder): fix current group when add group in the middle --- .../src/lib/query-builder/query-builder-tree.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index e3d35b4c1d2..ef4545067f4 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1655,21 +1655,21 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { } } - private init() { this.clearSelection(); this.cancelOperandAdd(); this.cancelOperandEdit(); - // Ignore values of 'parent' and 'hovered' properties for the comparison + // Ignore values of certain properties for the comparison + const propsToIgnore = ['parent', 'hovered', 'ignoreCase', 'inEditMode', 'inAddMode']; const propsReplacer = function replacer(key, value) { - if (key === "parent" || key === "hovered" || key === "ignoreCase" || key === "inEditMode") { + if (propsToIgnore.indexOf(key) >= 0) { return undefined; } else { return value; } }; - + // Skip root being recreated if the same const newRootGroup = this.createExpressionGroupItem(this.expressionTree); if (JSON.stringify(this.rootGroup, propsReplacer) !== JSON.stringify(newRootGroup, propsReplacer)) { From 9b266d157ee9f32e7a5c60fc6dfb0f9361bea6cc Mon Sep 17 00:00:00 2001 From: "INFRAGISTICS\\IPetrov" Date: Fri, 1 Nov 2024 15:16:47 +0200 Subject: [PATCH 145/147] test(query-builder): fix end group buttons test --- .../src/lib/query-builder/query-builder.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts index bccf848fcd2..949e6d0f1b0 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.spec.ts @@ -277,7 +277,7 @@ describe('IgxQueryBuilder', () => { // adding buttons should be enabled, 'end group' button should be disabled addingButtons = QueryBuilderFunctions.getQueryBuilderTreeRootGroupButtons(fix, 0); - expect(addingButtons.length).toBe(3); + expect(addingButtons.length).toBe(4); for (let i = 0; i < addingButtons.length; i++) { if (i === 3) { ControlsFunction.verifyButtonIsDisabled(addingButtons[i] as HTMLElement); From 902cfdb9527894009ed1565d8fac227995605a1f Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 1 Nov 2024 17:56:52 +0200 Subject: [PATCH 146/147] feat(query-builder): fix selected return fields when array is empty --- .../src/lib/query-builder/query-builder-tree.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index ef4545067f4..86093b39c24 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -228,6 +228,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ @Input() public set expressionTree(expressionTree: IExpressionTree) { + console.log('set'); this._expressionTree = expressionTree; if (!expressionTree) { this._selectedEntity = null; @@ -1461,7 +1462,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { this._selectedEntity = this.entities?.find(el => el.name === entityName); this._selectedReturnFields = - !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') + !expressionTree.returnFields || expressionTree.returnFields.includes('*') || expressionTree.returnFields.includes('All') || expressionTree.returnFields.length === 0 ? this.fields?.map(f => f.field) : this.fields?.filter(f => expressionTree.returnFields.indexOf(f.field) >= 0).map(f => f.field); } From 62e98ba5f976bce34e1aea39d46b26fd19df70a5 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 1 Nov 2024 18:22:11 +0200 Subject: [PATCH 147/147] chore(*): removed leftover console log --- .../src/lib/query-builder/query-builder-tree.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 86093b39c24..233b839778a 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -228,7 +228,6 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { */ @Input() public set expressionTree(expressionTree: IExpressionTree) { - console.log('set'); this._expressionTree = expressionTree; if (!expressionTree) { this._selectedEntity = null;